src/transform: Reimplement tests in WGSL Easier to read and write, and ensures that the tests exercise valid AST instead of synthetic structures that can never exist. Change-Id: I5d361ef96383c71943a424f5765952f21d740042 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/36422 Reviewed-by: dan sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@google.com> Auto-Submit: Ben Clayton <bclayton@google.com>
diff --git a/BUILD.gn b/BUILD.gn index 01684a9..547b74a 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -831,6 +831,7 @@ "src/transform/bound_array_accessors_test.cc", "src/transform/emit_vertex_point_size_test.cc", "src/transform/first_index_offset_test.cc", + "src/transform/test_helper.h", "src/transform/vertex_pulling_test.cc", "src/type_determiner_test.cc", "src/validator/validator_control_block_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a7c6a4..adc89af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt
@@ -465,10 +465,6 @@ scope_stack_test.cc symbol_table_test.cc symbol_test.cc - transform/emit_vertex_point_size_test.cc - transform/bound_array_accessors_test.cc - transform/first_index_offset_test.cc - transform/vertex_pulling_test.cc type_determiner_test.cc validator/validator_control_block_test.cc validator/validator_function_test.cc @@ -656,6 +652,16 @@ ) endif() + if(${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER}) + list(APPEND TINT_TEST_SRCS + transform/bound_array_accessors_test.cc + transform/emit_vertex_point_size_test.cc + transform/first_index_offset_test.cc + transform/test_helper.h + transform/vertex_pulling_test.cc + ) + endif() + if(${TINT_BUILD_MSL_WRITER}) list(APPEND TINT_TEST_SRCS writer/msl/generator_impl_alias_type_test.cc
diff --git a/src/transform/bound_array_accessors_test.cc b/src/transform/bound_array_accessors_test.cc index a3c148f..e7d70ce 100644 --- a/src/transform/bound_array_accessors_test.cc +++ b/src/transform/bound_array_accessors_test.cc
@@ -14,918 +14,400 @@ #include "src/transform/bound_array_accessors.h" -#include <memory> -#include <utility> - -#include "gtest/gtest.h" -#include "src/ast/array_accessor_expression.h" -#include "src/ast/binary_expression.h" -#include "src/ast/block_statement.h" -#include "src/ast/builder.h" -#include "src/ast/call_expression.h" -#include "src/ast/function.h" -#include "src/ast/identifier_expression.h" -#include "src/ast/module.h" -#include "src/ast/scalar_constructor_expression.h" -#include "src/ast/sint_literal.h" -#include "src/ast/storage_class.h" -#include "src/ast/type/array_type.h" -#include "src/ast/type/f32_type.h" -#include "src/ast/type/i32_type.h" -#include "src/ast/type/matrix_type.h" -#include "src/ast/type/pointer_type.h" -#include "src/ast/type/u32_type.h" -#include "src/ast/type/vector_type.h" -#include "src/ast/type/void_type.h" -#include "src/ast/type_constructor_expression.h" -#include "src/ast/uint_literal.h" -#include "src/ast/variable.h" -#include "src/ast/variable_decl_statement.h" -#include "src/diagnostic/formatter.h" -#include "src/transform/manager.h" -#include "src/type_determiner.h" +#include "src/transform/test_helper.h" namespace tint { namespace transform { namespace { -template <typename T = ast::Expression> -T* FindVariable(ast::Module* mod, std::string name) { - if (auto* func = mod->FindFunctionBySymbol(mod->RegisterSymbol("func"))) { - for (auto* stmt : *func->body()) { - if (auto* decl = stmt->As<ast::VariableDeclStatement>()) { - if (auto* var = decl->variable()) { - if (var->name() == name) { - return As<T>(var->constructor()); - } - } - } - } - } - return nullptr; -} - -class BoundArrayAccessorsTest : public testing::Test { - public: - ast::Module Transform(ast::Module in) { - TypeDeterminer td(&in); - if (!td.Determine()) { - error = "Type determination failed: " + td.error(); - return {}; - } - - Manager manager; - manager.append(std::make_unique<BoundArrayAccessors>()); - auto result = manager.Run(&in); - - if (result.diagnostics.contains_errors()) { - error = "manager().Run() errored:\n" + - diag::Formatter().format(result.diagnostics); - return {}; - } - - return std::move(result.module); - } - - std::string error; -}; - -struct ModuleBuilder : public ast::BuilderWithModule { - ast::Module Module() { - Build(); - mod->AddFunction(Func("func", ast::VariableList{}, ty.void_, statements, - ast::FunctionDecorationList{})); - return std::move(*mod); - } - - protected: - virtual void Build() = 0; - void OnVariableBuilt(ast::Variable* var) override { - statements.emplace_back(create<ast::VariableDeclStatement>(var)); - } - ast::StatementList statements; -}; +using BoundArrayAccessorsTest = TransformTest; TEST_F(BoundArrayAccessorsTest, Ptrs_Clamp) { - // var a : array<f32, 3>; - // const c : u32 = 1; - // const b : ptr<function, f32> = a[c] - // - // -> const b : ptr<function, i32> = a[min(u32(c), 2)] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); - Const("c", ast::StorageClass::kFunction, ty.u32); - Const("b", ast::StorageClass::kFunction, - ty.pointer<f32>(ast::StorageClass::kFunction), - IndexAccessor("a", "c"), {}); - } - }; + auto* src = R"( +var a : array<f32, 3>; +const c : u32 = 1u; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + const b : ptr<function, f32> = a[c]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : array<f32, 3>; +const c : u32 = 1u; - ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); +fn f() -> void { + const b : ptr<function, f32> = a[min(u32(c), 2u)]; +} +)"; - auto* idx = b->idx_expr()->As<ast::CallExpression>(); - ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); - EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_EQ(idx->params().size(), 2u); - - ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); - auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); - EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); - ASSERT_EQ(tc->values().size(), 1u); - ASSERT_TRUE(tc->values()[0]->Is<ast::IdentifierExpression>()); - ASSERT_EQ(tc->values()[0]->As<ast::IdentifierExpression>()->name(), "c"); - - ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); - auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Array_Idx_Nested_Scalar) { - // var a : array<f32, 3>; - // var b : array<f32, 5>; - // var i : u32; - // var c : f32 = a[b[i]]; - // - // -> var c : f32 = a[min(u32(b[min(u32(i), 4)]), 2)]; - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); - Var("b", ast::StorageClass::kFunction, ty.array<f32, 5>()); - Var("i", ast::StorageClass::kFunction, ty.u32); - Const("c", ast::StorageClass::kFunction, ty.f32, - IndexAccessor("a", IndexAccessor("b", "i")), {}); - } - }; + auto* src = R"( +var a : array<f32, 3>; +var b : array<f32, 5>; +var i : u32; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var c : f32 = a[ b[i] ]; +} +)"; - auto* c = FindVariable<ast::ArrayAccessorExpression>(&module, "c"); - ASSERT_NE(c, nullptr); + auto* expect = R"( +var a : array<f32, 3>; +var b : array<f32, 5>; +var i : u32; - ASSERT_TRUE(c->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(c->idx_expr()->Is<ast::CallExpression>()); +fn f() -> void { + var c : f32 = a[min(u32(b[min(u32(i), 4u)]), 2u)]; +} +)"; - auto* idx = c->idx_expr()->As<ast::CallExpression>(); - ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); - EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_EQ(idx->params().size(), 2u); - - ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); - auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); - EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); - ASSERT_EQ(tc->values().size(), 1u); - - auto* sub = tc->values()[0]; - ASSERT_TRUE(sub->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(sub->As<ast::ArrayAccessorExpression>() - ->idx_expr() - ->Is<ast::CallExpression>()); - - auto* sub_idx = sub->As<ast::ArrayAccessorExpression>() - ->idx_expr() - ->As<ast::CallExpression>(); - ASSERT_TRUE(sub_idx->func()->Is<ast::IdentifierExpression>()); - EXPECT_EQ(sub_idx->func()->As<ast::IdentifierExpression>()->name(), "min"); - - ASSERT_TRUE(sub_idx->params()[0]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(sub_idx->params()[0]->Is<ast::TypeConstructorExpression>()); - tc = sub_idx->params()[0]->As<ast::TypeConstructorExpression>(); - EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); - ASSERT_EQ(tc->values().size(), 1u); - ASSERT_TRUE(tc->values()[0]->Is<ast::IdentifierExpression>()); - ASSERT_EQ(tc->values()[0]->As<ast::IdentifierExpression>()->name(), "i"); - - ASSERT_TRUE(sub_idx->params()[1]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(sub_idx->params()[1]->Is<ast::ScalarConstructorExpression>()); - auto* scalar = sub_idx->params()[1]->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 4u); - - ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); - scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(c->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(c->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Array_Idx_Scalar) { - // var a : array<f32, 3> - // var b : f32 = a[1]; - // - // -> var b : f32 = a[1]; - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.array(ty.f32, 3)); - Var("b", ast::StorageClass::kFunction, ty.f32, IndexAccessor("a", 1u), - {}); - } - }; + auto* src = R"( +var a : array<f32, 3>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : array<f32, 3>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); +fn f() -> void { + var b : f32 = a[1]; +} +)"; - auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Array_Idx_Expr) { - // var a : array<f32, 3> - // var c : u32; - // var b : f32 = a[c + 2 - 3] - // - // -> var b : f32 = a[min(u32(c + 2 - 3), 2)] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); - Var("c", ast::StorageClass::kFunction, ty.u32); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor("a", Add("c", Sub(2u, 3u))), {}); - } - }; + auto* src = R"( +var a : array<f32, 3>; +var c : u32; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[c + 2 - 3]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : array<f32, 3>; +var c : u32; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); +fn f() -> void { + var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; +} +)"; - auto* idx = b->idx_expr()->As<ast::CallExpression>(); - ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); - EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_EQ(idx->params().size(), 2u); - - ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); - auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); - EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); - ASSERT_EQ(tc->values().size(), 1u); - auto* add = tc->values()[0]->As<ast::BinaryExpression>(); - ASSERT_NE(add, nullptr); - ASSERT_EQ(add->op(), ast::BinaryOp::kAdd); - auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); - ASSERT_NE(add_lhs, nullptr); - ASSERT_EQ(add_lhs->name(), "c"); - auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); - ASSERT_NE(add_rhs, nullptr); - ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->lhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 2u); - ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->rhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 3u); - - ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); - auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Array_Idx_Negative) { - // var a : array<f32, 3> - // var b : f32 = a[-1] - // - // -> var b : f32 = a[0] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); - Var("b", ast::StorageClass::kFunction, ty.f32, IndexAccessor("a", -1), - {}); - } - }; + auto* src = R"( +var a : array<f32, 3>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[-1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : array<f32, 3>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); +fn f() -> void { + var b : f32 = a[0]; +} +)"; - auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Array_Idx_OutOfBounds) { - // var a : array<f32, 3> - // var b : f32 = a[3] - // - // -> var b : f32 = a[2] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); - Var("b", ast::StorageClass::kFunction, ty.f32, IndexAccessor("a", 3u), - {}); - } - }; + auto* src = R"( +var a : array<f32, 3>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[3]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : array<f32, 3>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); +fn f() -> void { + var b : f32 = a[2]; +} +)"; - auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Vector_Idx_Scalar) { - // var a : vec3<f32> - // var b : f32 = a[1]; - // - // -> var b : f32 = a[1] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, IndexAccessor("a", 1u), - {}); - } - }; + auto* src = R"( +var a : vec3<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : vec3<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); +fn f() -> void { + var b : f32 = a[1]; +} +)"; - auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Vector_Idx_Expr) { - // var a : vec3<f32> - // var c : u32; - // var b : f32 = a[c + 2 - 3] - // - // -> var b : f32 = a[min(u32(c + 2 - 3), 2)] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); - Var("c", ast::StorageClass::kFunction, ty.u32); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor("a", Add("c", Sub(2u, 3u))), {}); - } - }; + auto* src = R"( +var a : vec3<f32>; +var c : u32; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[c + 2 - 3]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : vec3<f32>; +var c : u32; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); +fn f() -> void { + var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; +} +)"; - auto* idx = b->idx_expr()->As<ast::CallExpression>(); - ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); - EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_EQ(idx->params().size(), 2u); - ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); - auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); - EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); - ASSERT_EQ(tc->values().size(), 1u); - auto* add = tc->values()[0]->As<ast::BinaryExpression>(); - ASSERT_NE(add, nullptr); - auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); - ASSERT_NE(add_lhs, nullptr); - ASSERT_EQ(add_lhs->name(), "c"); - auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); - ASSERT_NE(add_rhs, nullptr); - ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->lhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 2u); - ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->rhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 3u); - - ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); - auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Vector_Idx_Negative) { - // var a : vec3<f32> - // var b : f32 = a[-1] - // - // -> var b : f32 = a[0] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, IndexAccessor("a", -1), - {}); - } - }; + auto* src = R"( +var a : vec3<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[-1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : vec3<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); +fn f() -> void { + var b : f32 = a[0]; +} +)"; - auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Vector_Idx_OutOfBounds) { - // var a : vec3<f32> - // var b : f32 = a[3] - // - // -> var b : f32 = a[2] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, IndexAccessor("a", 3u), - {}); - } - }; + auto* src = R"( +var a : vec3<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[3]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : vec3<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); +fn f() -> void { + var b : f32 = a[2]; +} +)"; - auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Scalar) { - // var a : mat3x2<f32> - // var b : f32 = a[2][1]; - // - // -> var b : f32 = a[2][1] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor(IndexAccessor("a", 2u), 1u), {}); - } - }; + auto* src = R"( +var a : mat3x2<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[2][1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : mat3x2<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); +fn f() -> void { + var b : f32 = a[2][1]; +} +)"; - ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); - auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); + auto got = Transform<BoundArrayAccessors>(src); - auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(ary->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); - - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); - - scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Column) { - // var a : mat3x2<f32> - // var c : u32; - // var b : f32 = a[c + 2 - 3][1] - // - // -> var b : f32 = a[min(u32(c + 2 - 3), 2)][1] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); - Var("c", ast::StorageClass::kFunction, ty.u32); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor(IndexAccessor("a", Add("c", Sub(2u, 3u))), 1u), {}); - } - }; + auto* src = R"( +var a : mat3x2<f32>; +var c : u32; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[c + 2 - 3][1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : mat3x2<f32>; +var c : u32; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); +fn f() -> void { + var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1]; +} +)"; - ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); - auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_TRUE(ary->idx_expr()->Is<ast::CallExpression>()); - auto* idx = ary->idx_expr()->As<ast::CallExpression>(); - ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); - EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); - - ASSERT_EQ(idx->params().size(), 2u); - - ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); - auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); - EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); - ASSERT_EQ(tc->values().size(), 1u); - auto* add = tc->values()[0]->As<ast::BinaryExpression>(); - ASSERT_NE(add, nullptr); - auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); - ASSERT_NE(add_lhs, nullptr); - ASSERT_EQ(add_lhs->name(), "c"); - auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); - ASSERT_NE(add_rhs, nullptr); - ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->lhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 2u); - ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->rhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 3u); - - ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); - auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(ary->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); - - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); - - scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Row) { - // var a : mat3x2<f32> - // var c : u32; - // var b : f32 = a[1][c + 2 - 3] - // - // -> var b : f32 = a[1][min(u32(c + 2 - 3), 1)] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); - Var("c", ast::StorageClass::kFunction, ty.u32); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor(IndexAccessor("a", 1u), Add("c", Sub(2u, 3u))), {}); - } - }; + auto* src = R"( +var a : mat3x2<f32>; +var c : u32; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[1][c + 2 - 3]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : mat3x2<f32>; +var c : u32; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); +fn f() -> void { + var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)]; +} +)"; - ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); - auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); + auto got = Transform<BoundArrayAccessors>(src); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); - - auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); - - ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); - auto* idx = b->idx_expr()->As<ast::CallExpression>(); - ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); - EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); - - ASSERT_EQ(idx->params().size(), 2u); - - ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); - auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); - EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); - ASSERT_EQ(tc->values().size(), 1u); - auto* add = tc->values()[0]->As<ast::BinaryExpression>(); - ASSERT_NE(add, nullptr); - auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); - ASSERT_NE(add_lhs, nullptr); - ASSERT_EQ(add_lhs->name(), "c"); - auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); - ASSERT_NE(add_rhs, nullptr); - ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->lhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 2u); - ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); - ASSERT_EQ(add_rhs->rhs() - ->As<ast::ScalarConstructorExpression>() - ->literal() - ->As<ast::UintLiteral>() - ->value(), - 3u); - - ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); - scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); - - ASSERT_NE(ary->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Negative_Column) { - // var a : mat3x2<f32> - // var b : f32 = a[-1][1] - // - // -> var b : f32 = a[0][1] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor(IndexAccessor("a", -1), 1), {}); - } - }; + auto* src = R"( +var a : mat3x2<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[-1][1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : mat3x2<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); +fn f() -> void { + var b : f32 = a[0][1]; +} +)"; - ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); - auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); + auto got = Transform<BoundArrayAccessors>(src); - auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); - - ASSERT_NE(ary->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::I32>()); - - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); - - scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 1); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Negative_Row) { - // var a : mat3x2<f32> - // var b : f32 = a[2][-1] - // - // -> var b : f32 = a[2][0] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor(IndexAccessor("a", 2), -1), {}); - } - }; + auto* src = R"( +var a : mat3x2<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[2][-1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : mat3x2<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); +fn f() -> void { + var b : f32 = a[2][0]; +} +)"; - ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); - auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); + auto got = Transform<BoundArrayAccessors>(src); - auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 2); - - ASSERT_NE(ary->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::I32>()); - - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); - - scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Matrix_Idx_OutOfBounds_Column) { - // var a : mat3x2<f32> - // var b : f32 = a[5][1] - // - // -> var b : f32 = a[2][1] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor(IndexAccessor("a", 5u), 1u), {}); - } - }; + auto* src = R"( +var a : mat3x2<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[5][1]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : mat3x2<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); +fn f() -> void { + var b : f32 = a[2][1]; +} +)"; - ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); - auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); + auto got = Transform<BoundArrayAccessors>(src); - auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(ary->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); - - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); - - scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } TEST_F(BoundArrayAccessorsTest, Matrix_Idx_OutOfBounds_Row) { - // var a : mat3x2<f32> - // var b : f32 = a[2][5] - // - // -> var b : f32 = a[2][1] - struct Builder : ModuleBuilder { - void Build() override { - Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); - Var("b", ast::StorageClass::kFunction, ty.f32, - IndexAccessor(IndexAccessor("a", 2u), 5u), {}); - } - }; + auto* src = R"( +var a : mat3x2<f32>; - ast::Module module = Transform(Builder{}.Module()); - ASSERT_EQ(error, ""); +fn f() -> void { + var b : f32 = a[2][5]; +} +)"; - auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); - ASSERT_NE(b, nullptr); + auto* expect = R"( +var a : mat3x2<f32>; - ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); +fn f() -> void { + var b : f32 = a[2][1]; +} +)"; - ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); - auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); + auto got = Transform<BoundArrayAccessors>(src); - auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); - - ASSERT_NE(ary->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); - - ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); - ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); - - scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); - ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); - EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); - - ASSERT_NE(b->idx_expr()->result_type(), nullptr); - ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); + EXPECT_EQ(expect, got); } // TODO(dsinclair): Implement when constant_id exists @@ -934,7 +416,7 @@ // var a : vec3<f32> // var b : f32 = a[idx] // - // ->var b : f32 = a[min(u32(idx), 2)] + // ->var b : f32 = a[min(u32(idx), 2)] } // TODO(dsinclair): Implement when constant_id exists
diff --git a/src/transform/emit_vertex_point_size_test.cc b/src/transform/emit_vertex_point_size_test.cc index c3c5f70..9a4a359 100644 --- a/src/transform/emit_vertex_point_size_test.cc +++ b/src/transform/emit_vertex_point_size_test.cc
@@ -14,203 +14,106 @@ #include "src/transform/emit_vertex_point_size.h" -#include <memory> -#include <utility> - -#include "gtest/gtest.h" -#include "src/ast/builder.h" -#include "src/ast/stage_decoration.h" -#include "src/ast/variable_decl_statement.h" -#include "src/demangler.h" -#include "src/diagnostic/formatter.h" -#include "src/transform/manager.h" +#include "src/transform/test_helper.h" namespace tint { namespace transform { namespace { -class EmitVertexPointSizeTest : public testing::Test { - public: - Transform::Output GetTransform(ast::Module in) { - Manager manager; - manager.append(std::make_unique<EmitVertexPointSize>()); - return manager.Run(&in); - } -}; - -struct ModuleBuilder : public ast::BuilderWithModule { - ModuleBuilder() {} - - ast::Module Module() { - Build(); - return std::move(*mod); - } - - protected: - virtual void Build() = 0; -}; +using EmitVertexPointSizeTest = TransformTest; TEST_F(EmitVertexPointSizeTest, VertexStageBasic) { - struct Builder : ModuleBuilder { - void Build() override { - mod->AddFunction(Func("non_entry_a", ast::VariableList{}, ty.void_, - ast::StatementList{}, - ast::FunctionDecorationList{})); + auto* src = R"( +fn non_entry_a() -> void { +} - auto* entry = - Func("entry", ast::VariableList{}, ty.void_, - ast::StatementList{ - create<ast::VariableDeclStatement>( - Var("builtin_assignments_should_happen_before_this", - tint::ast::StorageClass::kFunction, ty.f32)), - }, - ast::FunctionDecorationList{ - create<ast::StageDecoration>(ast::PipelineStage::kVertex), - }); - mod->AddFunction(entry); +[[stage(vertex)]] +fn entry() -> void { + var builtin_assignments_should_happen_before_this : f32; +} - mod->AddFunction(Func("non_entry_b", ast::VariableList{}, ty.void_, - ast::StatementList{}, - ast::FunctionDecorationList{})); - } - }; - - auto result = GetTransform(Builder{}.Module()); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto* expected = R"(Module{ - Variable{ - Decorations{ - BuiltinDecoration{pointsize} - } - tint_pointsize - out - __f32 - } - Function non_entry_a -> __void - () - { - } - Function entry -> __void - StageDecoration{vertex} - () - { - Assignment{ - Identifier[__ptr_out__f32]{tint_pointsize} - ScalarConstructor[__f32]{1.000000} - } - VariableDeclStatement{ - Variable{ - builtin_assignments_should_happen_before_this - function - __f32 - } - } - } - Function non_entry_b -> __void - () - { - } +fn non_entry_b() -> void { } )"; - EXPECT_EQ(expected, - Demangler().Demangle(result.module, result.module.to_str())); + + auto* expect = R"( +[[builtin(pointsize)]] var<out> tint_pointsize : f32; + +fn non_entry_a() -> void { +} + +[[stage(vertex)]] +fn entry() -> void { + tint_pointsize = 1.0; + var builtin_assignments_should_happen_before_this : f32; +} + +fn non_entry_b() -> void { +} +)"; + + auto got = Transform<EmitVertexPointSize>(src); + + EXPECT_EQ(expect, got); } TEST_F(EmitVertexPointSizeTest, VertexStageEmpty) { - struct Builder : ModuleBuilder { - void Build() override { - mod->AddFunction(Func("non_entry_a", ast::VariableList{}, ty.void_, - ast::StatementList{}, - ast::FunctionDecorationList{})); + auto* src = R"( +fn non_entry_a() -> void { +} - mod->AddFunction( - Func("entry", ast::VariableList{}, ty.void_, ast::StatementList{}, - ast::FunctionDecorationList{ - create<ast::StageDecoration>(ast::PipelineStage::kVertex), - })); +[[stage(vertex)]] +fn entry() -> void { +} - mod->AddFunction(Func("non_entry_b", ast::VariableList{}, ty.void_, - ast::StatementList{}, - ast::FunctionDecorationList{})); - } - }; - - auto result = GetTransform(Builder{}.Module()); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto* expected = R"(Module{ - Variable{ - Decorations{ - BuiltinDecoration{pointsize} - } - tint_pointsize - out - __f32 - } - Function non_entry_a -> __void - () - { - } - Function entry -> __void - StageDecoration{vertex} - () - { - Assignment{ - Identifier[__ptr_out__f32]{tint_pointsize} - ScalarConstructor[__f32]{1.000000} - } - } - Function non_entry_b -> __void - () - { - } +fn non_entry_b() -> void { } )"; - EXPECT_EQ(expected, - Demangler().Demangle(result.module, result.module.to_str())); + + auto* expect = R"( +[[builtin(pointsize)]] var<out> tint_pointsize : f32; + +fn non_entry_a() -> void { +} + +[[stage(vertex)]] +fn entry() -> void { + tint_pointsize = 1.0; +} + +fn non_entry_b() -> void { +} +)"; + + auto got = Transform<EmitVertexPointSize>(src); + + EXPECT_EQ(expect, got); } TEST_F(EmitVertexPointSizeTest, NonVertexStage) { - struct Builder : ModuleBuilder { - void Build() override { - auto* fragment_entry = Func( - "fragment_entry", ast::VariableList{}, ty.void_, ast::StatementList{}, - ast::FunctionDecorationList{ - create<ast::StageDecoration>(ast::PipelineStage::kFragment), - }); - mod->AddFunction(fragment_entry); + auto* src = R"( +[[stage(fragment)]] +fn fragment_entry() -> void { +} - auto* compute_entry = Func( - "compute_entry", ast::VariableList{}, ty.void_, ast::StatementList{}, - ast::FunctionDecorationList{ - create<ast::StageDecoration>(ast::PipelineStage::kCompute), - }); - mod->AddFunction(compute_entry); - } - }; - - auto result = GetTransform(Builder{}.Module()); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto* expected = R"(Module{ - Function fragment_entry -> __void - StageDecoration{fragment} - () - { - } - Function compute_entry -> __void - StageDecoration{compute} - () - { - } +[[stage(compute)]] +fn compute_entry() -> void { } )"; - EXPECT_EQ(expected, - Demangler().Demangle(result.module, result.module.to_str())); + + auto* expect = R"( +[[stage(fragment)]] +fn fragment_entry() -> void { +} + +[[stage(compute)]] +fn compute_entry() -> void { +} +)"; + + auto got = Transform<EmitVertexPointSize>(src); + + EXPECT_EQ(expect, got); } } // namespace
diff --git a/src/transform/first_index_offset_test.cc b/src/transform/first_index_offset_test.cc index 094df6d..c739810 100644 --- a/src/transform/first_index_offset_test.cc +++ b/src/transform/first_index_offset_test.cc
@@ -15,413 +15,223 @@ #include "src/transform/first_index_offset.h" #include <memory> -#include <string> #include <utility> +#include <vector> -#include "gtest/gtest.h" -#include "src/ast/block_statement.h" -#include "src/ast/builder.h" -#include "src/ast/builtin.h" -#include "src/ast/builtin_decoration.h" -#include "src/ast/call_expression.h" -#include "src/ast/call_statement.h" -#include "src/ast/function.h" -#include "src/ast/identifier_expression.h" -#include "src/ast/module.h" -#include "src/ast/return_statement.h" -#include "src/ast/storage_class.h" -#include "src/ast/type/u32_type.h" -#include "src/ast/variable.h" -#include "src/ast/variable_decoration.h" -#include "src/demangler.h" -#include "src/diagnostic/formatter.h" -#include "src/source.h" -#include "src/transform/manager.h" +#include "src/transform/test_helper.h" namespace tint { namespace transform { namespace { -class FirstIndexOffsetTest : public testing::Test {}; - -struct ModuleBuilder : public ast::BuilderWithModule { - ast::Module Module() { - Build(); - return std::move(*mod); - } - - protected: - void AddBuiltinInput(const std::string& name, ast::Builtin builtin) { - mod->AddGlobalVariable(Var(name, ast::StorageClass::kInput, ty.u32, nullptr, - {create<ast::BuiltinDecoration>(builtin)})); - } - - ast::Function* AddFunction(const std::string& name, - ast::StatementList stmts) { - auto* func = Func(name, ast::VariableList{}, ty.u32, stmts, - ast::FunctionDecorationList{}); - mod->AddFunction(func); - return func; - } - - virtual void Build() = 0; -}; +using FirstIndexOffsetTest = TransformTest; TEST_F(FirstIndexOffsetTest, Error_AlreadyTransformed) { - struct Builder : public ModuleBuilder { - void Build() override { - AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx); - AddFunction("test", {create<ast::ReturnStatement>(Expr("vert_idx"))}); - } - }; + auto* src = R"( +[[builtin(vertex_idx)]] var<in> vert_idx : u32; - Manager manager; - manager.append(std::make_unique<FirstIndexOffset>(0, 0)); - manager.append(std::make_unique<FirstIndexOffset>(1, 1)); +fn test() -> u32 { + return vert_idx; +} - auto module = Builder{}.Module(); - auto result = manager.Run(&module); +[[stage(vertex)]] +fn entry() -> void { + test(); +} +)"; - // Release the source module to ensure there's no uncloned data in result - { auto tmp = std::move(module); } + auto* expect = R"(manager().Run() errored: +error: First index offset transform has already been applied.)"; - ASSERT_EQ(diag::Formatter().format(result.diagnostics), - "error: First index offset transform has already been applied."); + std::vector<std::unique_ptr<transform::Transform>> transforms; + transforms.emplace_back(std::make_unique<FirstIndexOffset>(0, 0)); + transforms.emplace_back(std::make_unique<FirstIndexOffset>(1, 1)); + + auto got = Transform(src, std::move(transforms)); + + EXPECT_EQ(expect, got); } TEST_F(FirstIndexOffsetTest, EmptyModule) { - Manager manager; - manager.append(std::make_unique<FirstIndexOffset>(0, 0)); + auto* src = ""; + auto* expect = ""; - ast::Module module; - auto result = manager.Run(&module); + auto got = Transform<FirstIndexOffset>(src, 0, 0); - // Release the source module to ensure there's no uncloned data in result - { auto tmp = std::move(module); } - - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto got = result.module.to_str(); - auto* expected = "Module{\n}\n"; - EXPECT_EQ(got, expected); + EXPECT_EQ(expect, got); } TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex) { - struct Builder : public ModuleBuilder { - void Build() override { - AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx); - AddFunction("test", {create<ast::ReturnStatement>(Expr("vert_idx"))}); - } - }; + auto* src = R"( +[[builtin(vertex_idx)]] var<in> vert_idx : u32; - Manager manager; - manager.append(std::make_unique<FirstIndexOffset>(1, 2)); +fn test() -> u32 { + return vert_idx; +} - auto module = Builder{}.Module(); - auto result = manager.Run(&module); - - // Release the source module to ensure there's no uncloned data in result - { auto tmp = std::move(module); } - - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto got = result.module.to_str(); - auto* expected = - R"(Module{ - TintFirstIndexOffsetData Struct{ - [[block]] - StructMember{[[ offset 0 ]] tint_first_vertex_index: __u32} - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - tint_first_index_offset_vert_idx - in - __u32 - } - Variable{ - Decorations{ - BindingDecoration{1} - SetDecoration{2} - } - tint_first_index_data - uniform - __struct_TintFirstIndexOffsetData - } - Function test -> __u32 - () - { - VariableDeclStatement{ - VariableConst{ - vert_idx - none - __u32 - { - Binary[__u32]{ - Identifier[__ptr_in__u32]{tint_first_index_offset_vert_idx} - add - MemberAccessor[__ptr_uniform__u32]{ - Identifier[__ptr_uniform__struct_TintFirstIndexOffsetData]{tint_first_index_data} - Identifier[not set]{tint_first_vertex_index} - } - } - } - } - } - Return{ - { - Identifier[__u32]{vert_idx} - } - } - } +[[stage(vertex)]] +fn entry() -> void { + test(); } )"; - EXPECT_EQ(Demangler().Demangle(result.module, got), expected); + + auto* expect = R"( +[[block]] +struct TintFirstIndexOffsetData { + [[offset(0)]] + tint_first_vertex_index : u32; +}; + +[[builtin(vertex_idx)]] var<in> tint_first_index_offset_vert_idx : u32; +[[binding(1), set(2)]] var<uniform> tint_first_index_data : TintFirstIndexOffsetData; + +fn test() -> u32 { + const vert_idx : u32 = (tint_first_index_offset_vert_idx + tint_first_index_data.tint_first_vertex_index); + return vert_idx; +} + +[[stage(vertex)]] +fn entry() -> void { + test(); +} +)"; + + auto got = Transform<FirstIndexOffset>(src, 1, 2); + + EXPECT_EQ(expect, got); } TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex) { - struct Builder : public ModuleBuilder { - void Build() override { - AddBuiltinInput("inst_idx", ast::Builtin::kInstanceIdx); - AddFunction("test", {create<ast::ReturnStatement>(Expr("inst_idx"))}); - } - }; + auto* src = R"( +[[builtin(instance_idx)]] var<in> inst_idx : u32; - Manager manager; - manager.append(std::make_unique<FirstIndexOffset>(1, 7)); +fn test() -> u32 { + return inst_idx; +} - auto module = Builder{}.Module(); - auto result = manager.Run(&module); - - // Release the source module to ensure there's no uncloned data in result - { auto tmp = std::move(module); } - - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto got = result.module.to_str(); - auto* expected = R"(Module{ - TintFirstIndexOffsetData Struct{ - [[block]] - StructMember{[[ offset 0 ]] tint_first_instance_index: __u32} - } - Variable{ - Decorations{ - BuiltinDecoration{instance_idx} - } - tint_first_index_offset_inst_idx - in - __u32 - } - Variable{ - Decorations{ - BindingDecoration{1} - SetDecoration{7} - } - tint_first_index_data - uniform - __struct_TintFirstIndexOffsetData - } - Function test -> __u32 - () - { - VariableDeclStatement{ - VariableConst{ - inst_idx - none - __u32 - { - Binary[__u32]{ - Identifier[__ptr_in__u32]{tint_first_index_offset_inst_idx} - add - MemberAccessor[__ptr_uniform__u32]{ - Identifier[__ptr_uniform__struct_TintFirstIndexOffsetData]{tint_first_index_data} - Identifier[not set]{tint_first_instance_index} - } - } - } - } - } - Return{ - { - Identifier[__u32]{inst_idx} - } - } - } +[[stage(vertex)]] +fn entry() -> void { + test(); } )"; - EXPECT_EQ(Demangler().Demangle(result.module, got), expected); + + auto* expect = R"( +[[block]] +struct TintFirstIndexOffsetData { + [[offset(0)]] + tint_first_instance_index : u32; +}; + +[[builtin(instance_idx)]] var<in> tint_first_index_offset_inst_idx : u32; +[[binding(1), set(7)]] var<uniform> tint_first_index_data : TintFirstIndexOffsetData; + +fn test() -> u32 { + const inst_idx : u32 = (tint_first_index_offset_inst_idx + tint_first_index_data.tint_first_instance_index); + return inst_idx; +} + +[[stage(vertex)]] +fn entry() -> void { + test(); +} +)"; + + auto got = Transform<FirstIndexOffset>(src, 1, 7); + + EXPECT_EQ(expect, got); } TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex) { - struct Builder : public ModuleBuilder { - void Build() override { - AddBuiltinInput("inst_idx", ast::Builtin::kInstanceIdx); - AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx); - AddFunction("test", { - create<ast::ReturnStatement>(Expr(1u)), - }); - } - }; + auto* src = R"( +[[builtin(instance_idx)]] var<in> instance_idx : u32; +[[builtin(vertex_idx)]] var<in> vert_idx : u32; - auto transform = std::make_unique<FirstIndexOffset>(1, 7); - auto* transform_ptr = transform.get(); +fn test() -> u32 { + return instance_idx + vert_idx; +} - Manager manager; - manager.append(std::move(transform)); - - auto module = Builder{}.Module(); - auto result = manager.Run(&module); - - // Release the source module to ensure there's no uncloned data in result - { auto tmp = std::move(module); } - - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto got = result.module.to_str(); - auto* expected = R"(Module{ - TintFirstIndexOffsetData Struct{ - [[block]] - StructMember{[[ offset 0 ]] tint_first_vertex_index: __u32} - StructMember{[[ offset 4 ]] tint_first_instance_index: __u32} - } - Variable{ - Decorations{ - BuiltinDecoration{instance_idx} - } - tint_first_index_offset_inst_idx - in - __u32 - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - tint_first_index_offset_vert_idx - in - __u32 - } - Variable{ - Decorations{ - BindingDecoration{1} - SetDecoration{7} - } - tint_first_index_data - uniform - __struct_TintFirstIndexOffsetData - } - Function test -> __u32 - () - { - Return{ - { - ScalarConstructor[__u32]{1} - } - } - } +[[stage(vertex)]] +fn entry() -> void { + test(); } )"; - EXPECT_EQ(Demangler().Demangle(result.module, got), expected); - EXPECT_TRUE(transform_ptr->HasVertexIndex()); - EXPECT_EQ(transform_ptr->GetFirstVertexOffset(), 0u); + auto* expect = R"( +[[block]] +struct TintFirstIndexOffsetData { + [[offset(0)]] + tint_first_vertex_index : u32; + [[offset(4)]] + tint_first_instance_index : u32; +}; - EXPECT_TRUE(transform_ptr->HasInstanceIndex()); - EXPECT_EQ(transform_ptr->GetFirstInstanceOffset(), 4u); +[[builtin(instance_idx)]] var<in> tint_first_index_offset_instance_idx : u32; +[[builtin(vertex_idx)]] var<in> tint_first_index_offset_vert_idx : u32; +[[binding(1), set(2)]] var<uniform> tint_first_index_data : TintFirstIndexOffsetData; + +fn test() -> u32 { + const instance_idx : u32 = (tint_first_index_offset_instance_idx + tint_first_index_data.tint_first_instance_index); + const vert_idx : u32 = (tint_first_index_offset_vert_idx + tint_first_index_data.tint_first_vertex_index); + return (instance_idx + vert_idx); +} + +[[stage(vertex)]] +fn entry() -> void { + test(); +} +)"; + + auto got = Transform<FirstIndexOffset>(src, 1, 2); + + EXPECT_EQ(expect, got); } TEST_F(FirstIndexOffsetTest, NestedCalls) { - struct Builder : public ModuleBuilder { - void Build() override { - AddBuiltinInput("vert_idx", ast::Builtin::kVertexIdx); - AddFunction("func1", {create<ast::ReturnStatement>(Expr("vert_idx"))}); - AddFunction("func2", {create<ast::ReturnStatement>(Call("func1"))}); - } - }; + auto* src = R"( +[[builtin(vertex_idx)]] var<in> vert_idx : u32; - auto transform = std::make_unique<FirstIndexOffset>(2, 2); +fn func1() -> u32 { + return vert_idx; +} - Manager manager; - manager.append(std::move(transform)); +fn func2() -> u32 { + return func1(); +} - auto module = Builder{}.Module(); - auto result = manager.Run(&module); - - // Release the source module to ensure there's no uncloned data in result - { auto tmp = std::move(module); } - - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - auto got = result.module.to_str(); - auto* expected = R"(Module{ - TintFirstIndexOffsetData Struct{ - [[block]] - StructMember{[[ offset 0 ]] tint_first_vertex_index: __u32} - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - tint_first_index_offset_vert_idx - in - __u32 - } - Variable{ - Decorations{ - BindingDecoration{2} - SetDecoration{2} - } - tint_first_index_data - uniform - __struct_TintFirstIndexOffsetData - } - Function func1 -> __u32 - () - { - VariableDeclStatement{ - VariableConst{ - vert_idx - none - __u32 - { - Binary[__u32]{ - Identifier[__ptr_in__u32]{tint_first_index_offset_vert_idx} - add - MemberAccessor[__ptr_uniform__u32]{ - Identifier[__ptr_uniform__struct_TintFirstIndexOffsetData]{tint_first_index_data} - Identifier[not set]{tint_first_vertex_index} - } - } - } - } - } - Return{ - { - Identifier[__u32]{vert_idx} - } - } - } - Function func2 -> __u32 - () - { - Return{ - { - Call[__u32]{ - Identifier[__u32]{func1} - ( - ) - } - } - } - } +[[stage(vertex)]] +fn entry() -> void { + func2(); } )"; - EXPECT_EQ(Demangler().Demangle(result.module, got), expected); + + auto* expect = R"( +[[block]] +struct TintFirstIndexOffsetData { + [[offset(0)]] + tint_first_vertex_index : u32; +}; + +[[builtin(vertex_idx)]] var<in> tint_first_index_offset_vert_idx : u32; +[[binding(1), set(2)]] var<uniform> tint_first_index_data : TintFirstIndexOffsetData; + +fn func1() -> u32 { + const vert_idx : u32 = (tint_first_index_offset_vert_idx + tint_first_index_data.tint_first_vertex_index); + return vert_idx; +} + +fn func2() -> u32 { + return func1(); +} + +[[stage(vertex)]] +fn entry() -> void { + func2(); +} +)"; + + auto got = Transform<FirstIndexOffset>(src, 1, 2); + + EXPECT_EQ(expect, got); } } // namespace
diff --git a/src/transform/test_helper.h b/src/transform/test_helper.h new file mode 100644 index 0000000..269d8a9 --- /dev/null +++ b/src/transform/test_helper.h
@@ -0,0 +1,115 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_TRANSFORM_TEST_HELPER_H_ +#define SRC_TRANSFORM_TEST_HELPER_H_ + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "gtest/gtest.h" +#include "src/reader/wgsl/parser.h" +#include "src/transform/manager.h" +#include "src/type_determiner.h" +#include "src/writer/wgsl/generator.h" + +namespace tint { +namespace transform { + +/// Helper class for testing transforms +class TransformTest : public testing::Test { + public: + /// Transforms and returns the WGSL source `in`, transformed using + /// `transforms`. + /// @param in the input WGSL source + /// @param transforms the list of transforms to apply + /// @return the transformed WGSL output + std::string Transform( + std::string in, + std::vector<std::unique_ptr<transform::Transform>> transforms) { + Source::File file("test", in); + reader::wgsl::Parser parser(&file); + if (!parser.Parse()) { + return "WGSL reader failed:\n" + parser.error(); + } + + auto module = parser.module(); + TypeDeterminer td(&module); + if (!td.Determine()) { + return "Type determination failed:\n" + td.error(); + } + + Manager manager; + for (auto& transform : transforms) { + manager.append(std::move(transform)); + } + auto result = manager.Run(&module); + + if (result.diagnostics.contains_errors()) { + return "manager().Run() errored:\n" + + diag::Formatter().format(result.diagnostics); + } + + // Release the source module to ensure there's no uncloned data in result + { auto tmp = std::move(module); } + + writer::wgsl::Generator generator(std::move(result.module)); + if (!generator.Generate()) { + return "WGSL writer failed:\n" + generator.error(); + } + + auto res = generator.result(); + if (res.empty()) { + return res; + } + // The WGSL sometimes has two trailing newlines. Strip them + while (res.back() == '\n') { + res.pop_back(); + } + if (res.empty()) { + return res; + } + return "\n" + res + "\n"; + } + + /// Transforms and returns the WGSL source `in`, transformed using + /// `transform`. + /// @param transform the transform to apply + /// @param in the input WGSL source + /// @return the transformed WGSL output + std::string Transform(std::string in, + std::unique_ptr<transform::Transform> transform) { + std::vector<std::unique_ptr<transform::Transform>> transforms; + transforms.emplace_back(std::move(transform)); + return Transform(std::move(in), std::move(transforms)); + } + + /// Transforms and returns the WGSL source `in`, transformed using + /// a transform of type `TRANSFORM`. + /// @param in the input WGSL source + /// @param args the TRANSFORM constructor arguments + /// @return the transformed WGSL output + template <typename TRANSFORM, typename... ARGS> + std::string Transform(std::string in, ARGS&&... args) { + return Transform(std::move(in), + std::make_unique<TRANSFORM>(std::forward<ARGS>(args)...)); + } +}; + +} // namespace transform +} // namespace tint + +#endif // SRC_TRANSFORM_TEST_HELPER_H_
diff --git a/src/transform/vertex_pulling_test.cc b/src/transform/vertex_pulling_test.cc index 129a4f9..87e9a0c 100644 --- a/src/transform/vertex_pulling_test.cc +++ b/src/transform/vertex_pulling_test.cc
@@ -16,1008 +16,369 @@ #include <utility> -#include "gtest/gtest.h" -#include "src/ast/builder.h" -#include "src/ast/function.h" -#include "src/ast/pipeline_stage.h" -#include "src/ast/stage_decoration.h" -#include "src/ast/type/array_type.h" -#include "src/ast/type/f32_type.h" -#include "src/ast/type/i32_type.h" -#include "src/ast/type/void_type.h" -#include "src/demangler.h" -#include "src/diagnostic/formatter.h" -#include "src/transform/manager.h" -#include "src/type_determiner.h" -#include "src/validator/validator.h" +#include "src/transform/test_helper.h" namespace tint { namespace transform { namespace { -class VertexPullingHelper : public ast::BuilderWithModule { - public: - VertexPullingHelper() { - manager_ = std::make_unique<Manager>(); - auto transform = std::make_unique<VertexPulling>(); - transform_ = transform.get(); - manager_->append(std::move(transform)); - } - - // Create basic module with an entry point and vertex function - void InitBasicModule() { - auto* func = - Func("main", ast::VariableList{}, ty.void_, ast::StatementList{}, - ast::FunctionDecorationList{ - create<ast::StageDecoration>(ast::PipelineStage::kVertex)}); - - mod->AddFunction(func); - } - - // Set up the transformation, after building the module - void InitTransform(VertexStateDescriptor vertex_state) { - EXPECT_TRUE(mod->IsValid()); - - TypeDeterminer td(mod); - EXPECT_TRUE(td.Determine()); - - transform_->SetVertexState(vertex_state); - transform_->SetEntryPoint("main"); - } - - // Inserts a variable which will be converted to vertex pulling - void AddVertexInputVariable(uint32_t location, - std::string name, - ast::type::Type* type) { - auto* var = Var(name, ast::StorageClass::kInput, type, nullptr, - ast::VariableDecorationList{ - create<ast::LocationDecoration>(location), - }); - - mod->AddGlobalVariable(var); - } - - Manager* manager() { return manager_.get(); } - VertexPulling* transform() { return transform_; } - - private: - std::unique_ptr<Manager> manager_; - VertexPulling* transform_; -}; - -class VertexPullingTest : public VertexPullingHelper, public testing::Test {}; +using VertexPullingTest = TransformTest; TEST_F(VertexPullingTest, Error_NoVertexState) { - auto result = manager()->Run(mod); - EXPECT_TRUE(result.diagnostics.contains_errors()); - EXPECT_EQ(diag::Formatter().format(result.diagnostics), - "error: SetVertexState not called"); + auto* src = R"( +[[stage(vertex)]] +fn main() -> void {} +)"; + + auto* expect = R"(manager().Run() errored: +error: SetVertexState not called)"; + + auto got = Transform<VertexPulling>(src); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, Error_NoEntryPoint) { - transform()->SetVertexState({}); - auto result = manager()->Run(mod); - EXPECT_TRUE(result.diagnostics.contains_errors()); - EXPECT_EQ(diag::Formatter().format(result.diagnostics), - "error: Vertex stage entry point not found"); + auto* src = ""; + + auto* expect = R"(manager().Run() errored: +error: Vertex stage entry point not found)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState({}); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, Error_InvalidEntryPoint) { - InitBasicModule(); - InitTransform({}); - transform()->SetEntryPoint("_"); + auto* src = R"( +[[stage(vertex)]] +fn main() -> void {} +)"; - auto result = manager()->Run(mod); - EXPECT_TRUE(result.diagnostics.contains_errors()); - EXPECT_EQ(diag::Formatter().format(result.diagnostics), - "error: Vertex stage entry point not found"); + auto* expect = R"(manager().Run() errored: +error: Vertex stage entry point not found)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState({}); + transform->SetEntryPoint("_"); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, Error_EntryPointWrongStage) { - auto* func = - Func("main", ast::VariableList{}, ty.void_, ast::StatementList{}, - ast::FunctionDecorationList{ - create<ast::StageDecoration>(ast::PipelineStage::kFragment), - }); - mod->AddFunction(func); + auto* src = R"( +[[stage(fragment)]] +fn main() -> void {} +)"; - InitTransform({}); - auto result = manager()->Run(mod); - EXPECT_TRUE(result.diagnostics.contains_errors()); - EXPECT_EQ(diag::Formatter().format(result.diagnostics), - "error: Vertex stage entry point not found"); + auto* expect = R"(manager().Run() errored: +error: Vertex stage entry point not found)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState({}); + transform->SetEntryPoint("main"); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, BasicModule) { - InitBasicModule(); - InitTransform({}); - auto result = manager()->Run(mod); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); + auto* src = R"( +[[stage(vertex)]] +fn main() -> void {} +)"; + + auto* expect = R"( +[[block]] +struct TintVertexData { + [[offset(0)]] + _tint_vertex_data : [[stride(4)]] array<u32>; +}; + +[[stage(vertex)]] +fn main() -> void { + { + var _tint_pulling_pos : i32; + } +} +)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState({}); + transform->SetEntryPoint("main"); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, OneAttribute) { - InitBasicModule(); + auto* src = R"( +[[location(0)]] var<in> var_a : f32; - AddVertexInputVariable(0, "var_a", ty.f32); +[[stage(vertex)]] +fn main() -> void {} +)"; - InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}); + auto* expect = R"( +[[block]] +struct TintVertexData { + [[offset(0)]] + _tint_vertex_data : [[stride(4)]] array<u32>; +}; - auto result = manager()->Run(mod); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); +[[builtin(vertex_idx)]] var<in> _tint_pulling_vertex_index : i32; +[[binding(0), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_0 : TintVertexData; +var<private> var_a : f32; - EXPECT_EQ(R"(Module{ - TintVertexData Struct{ - [[block]] - StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4} - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - _tint_pulling_vertex_index - in - __i32 - } - Variable{ - Decorations{ - BindingDecoration{0} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_0 - storage_buffer - __struct_TintVertexData - } - Variable{ - var_a - private - __f32 - } - Function main -> __void - StageDecoration{vertex} - () +[[stage(vertex)]] +fn main() -> void { { - Block{ - VariableDeclStatement{ - Variable{ - _tint_pulling_pos - function - __i32 - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_vertex_index} - multiply - ScalarConstructor[__u32]{4} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__f32]{var_a} - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } + var _tint_pulling_pos : i32; + _tint_pulling_pos = ((_tint_pulling_vertex_index * 4u) + 0u); + var_a = bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[(_tint_pulling_pos / 4u)]); } } -)", - Demangler().Demangle(result.module, result.module.to_str())); +)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState( + {{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}); + transform->SetEntryPoint("main"); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, OneInstancedAttribute) { - InitBasicModule(); + auto* src = R"( +[[location(0)]] var<in> var_a : f32; - AddVertexInputVariable(0, "var_a", ty.f32); +[[stage(vertex)]] +fn main() -> void {} +)"; - InitTransform( - {{{4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 0}}}}}); + auto* expect = R"( +[[block]] +struct TintVertexData { + [[offset(0)]] + _tint_vertex_data : [[stride(4)]] array<u32>; +}; - auto result = manager()->Run(mod); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); +[[builtin(instance_idx)]] var<in> _tint_pulling_instance_index : i32; +[[binding(0), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_0 : TintVertexData; +var<private> var_a : f32; - EXPECT_EQ(R"(Module{ - TintVertexData Struct{ - [[block]] - StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4} - } - Variable{ - Decorations{ - BuiltinDecoration{instance_idx} - } - _tint_pulling_instance_index - in - __i32 - } - Variable{ - Decorations{ - BindingDecoration{0} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_0 - storage_buffer - __struct_TintVertexData - } - Variable{ - var_a - private - __f32 - } - Function main -> __void - StageDecoration{vertex} - () +[[stage(vertex)]] +fn main() -> void { { - Block{ - VariableDeclStatement{ - Variable{ - _tint_pulling_pos - function - __i32 - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_instance_index} - multiply - ScalarConstructor[__u32]{4} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__f32]{var_a} - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } + var _tint_pulling_pos : i32; + _tint_pulling_pos = ((_tint_pulling_instance_index * 4u) + 0u); + var_a = bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[(_tint_pulling_pos / 4u)]); } } -)", - Demangler().Demangle(result.module, result.module.to_str())); +)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState( + {{{4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 0}}}}}); + transform->SetEntryPoint("main"); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, OneAttributeDifferentOutputSet) { - InitBasicModule(); + auto* src = R"( +[[location(0)]] var<in> var_a : f32; - AddVertexInputVariable(0, "var_a", ty.f32); +[[stage(vertex)]] +fn main() -> void {} +)"; - InitTransform({{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}); - transform()->SetPullingBufferBindingSet(5); + auto* expect = R"( +[[block]] +struct TintVertexData { + [[offset(0)]] + _tint_vertex_data : [[stride(4)]] array<u32>; +}; - auto result = manager()->Run(mod); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); +[[builtin(vertex_idx)]] var<in> _tint_pulling_vertex_index : i32; +[[binding(0), set(5)]] var<storage_buffer> _tint_pulling_vertex_buffer_0 : TintVertexData; +var<private> var_a : f32; - EXPECT_EQ(R"(Module{ - TintVertexData Struct{ - [[block]] - StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4} - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - _tint_pulling_vertex_index - in - __i32 - } - Variable{ - Decorations{ - BindingDecoration{0} - SetDecoration{5} - } - _tint_pulling_vertex_buffer_0 - storage_buffer - __struct_TintVertexData - } - Variable{ - var_a - private - __f32 - } - Function main -> __void - StageDecoration{vertex} - () +[[stage(vertex)]] +fn main() -> void { { - Block{ - VariableDeclStatement{ - Variable{ - _tint_pulling_pos - function - __i32 - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_vertex_index} - multiply - ScalarConstructor[__u32]{4} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__f32]{var_a} - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } + var _tint_pulling_pos : i32; + _tint_pulling_pos = ((_tint_pulling_vertex_index * 4u) + 0u); + var_a = bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[(_tint_pulling_pos / 4u)]); } } -)", - Demangler().Demangle(result.module, result.module.to_str())); +)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState( + {{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}}}); + transform->SetPullingBufferBindingSet(5); + transform->SetEntryPoint("main"); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } // We expect the transform to use an existing builtin variables if it finds them TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex) { - InitBasicModule(); + auto* src = R"( +[[location(0)]] var<in> var_a : f32; +[[location(1)]] var<in> var_b : f32; +[[builtin(vertex_idx)]] var<in> custom_vertex_index : i32; +[[builtin(instance_idx)]] var<in> custom_instance_index : i32; - AddVertexInputVariable(0, "var_a", ty.f32); - AddVertexInputVariable(1, "var_b", ty.f32); +[[stage(vertex)]] +fn main() -> void {} +)"; - mod->AddGlobalVariable( - Var("custom_vertex_index", ast::StorageClass::kInput, ty.i32, nullptr, - ast::VariableDecorationList{ - create<ast::BuiltinDecoration>(ast::Builtin::kVertexIdx), - })); + auto* expect = R"( +[[block]] +struct TintVertexData { + [[offset(0)]] + _tint_vertex_data : [[stride(4)]] array<u32>; +}; - mod->AddGlobalVariable( - Var("custom_instance_index", ast::StorageClass::kInput, ty.i32, nullptr, - ast::VariableDecorationList{ - create<ast::BuiltinDecoration>(ast::Builtin::kInstanceIdx), - })); +[[binding(0), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_0 : TintVertexData; +[[binding(1), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_1 : TintVertexData; +var<private> var_a : f32; +var<private> var_b : f32; +[[builtin(vertex_idx)]] var<in> custom_vertex_index : i32; +[[builtin(instance_idx)]] var<in> custom_instance_index : i32; - InitTransform( - {{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}, - {4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}}}}); - - auto result = manager()->Run(mod); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); - - EXPECT_EQ(R"(Module{ - TintVertexData Struct{ - [[block]] - StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4} - } - Variable{ - Decorations{ - BindingDecoration{0} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_0 - storage_buffer - __struct_TintVertexData - } - Variable{ - Decorations{ - BindingDecoration{1} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_1 - storage_buffer - __struct_TintVertexData - } - Variable{ - var_a - private - __f32 - } - Variable{ - var_b - private - __f32 - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - custom_vertex_index - in - __i32 - } - Variable{ - Decorations{ - BuiltinDecoration{instance_idx} - } - custom_instance_index - in - __i32 - } - Function main -> __void - StageDecoration{vertex} - () +[[stage(vertex)]] +fn main() -> void { { - Block{ - VariableDeclStatement{ - Variable{ - _tint_pulling_pos - function - __i32 - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{custom_vertex_index} - multiply - ScalarConstructor[__u32]{4} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__f32]{var_a} - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - divide - ScalarConstructor[__u32]{4} - } - } - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{custom_instance_index} - multiply - ScalarConstructor[__u32]{4} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__f32]{var_b} - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_1} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } + var _tint_pulling_pos : i32; + _tint_pulling_pos = ((custom_vertex_index * 4u) + 0u); + var_a = bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[(_tint_pulling_pos / 4u)]); + _tint_pulling_pos = ((custom_instance_index * 4u) + 0u); + var_b = bitcast<f32>(_tint_pulling_vertex_buffer_1._tint_vertex_data[(_tint_pulling_pos / 4u)]); } } -)", - Demangler().Demangle(result.module, result.module.to_str())); +)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState( + {{{4, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}}}, + {4, InputStepMode::kInstance, {{VertexFormat::kF32, 0, 1}}}}}); + transform->SetEntryPoint("main"); + + auto got = Transform(src, std::move(transform)); + + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, TwoAttributesSameBuffer) { - InitBasicModule(); + auto* src = R"( +[[location(0)]] var<in> var_a : f32; +[[location(1)]] var<in> var_b : array<f32, 4>; - AddVertexInputVariable(0, "var_a", ty.f32); - AddVertexInputVariable(1, "var_b", ty.array<f32, 4>()); +[[stage(vertex)]] +fn main() -> void {} +)"; - InitTransform( + auto* expect = R"( +[[block]] +struct TintVertexData { + [[offset(0)]] + _tint_vertex_data : [[stride(4)]] array<u32>; +}; + +[[builtin(vertex_idx)]] var<in> _tint_pulling_vertex_index : i32; +[[binding(0), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_0 : TintVertexData; +var<private> var_a : f32; +var<private> var_b : array<f32, 4>; + +[[stage(vertex)]] +fn main() -> void { + { + var _tint_pulling_pos : i32; + _tint_pulling_pos = ((_tint_pulling_vertex_index * 16u) + 0u); + var_a = bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[(_tint_pulling_pos / 4u)]); + _tint_pulling_pos = ((_tint_pulling_vertex_index * 16u) + 0u); + var_b = vec4<f32>(bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[((_tint_pulling_pos + 0u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[((_tint_pulling_pos + 4u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[((_tint_pulling_pos + 8u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[((_tint_pulling_pos + 12u) / 4u)])); + } +} +)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState( {{{16, InputStepMode::kVertex, {{VertexFormat::kF32, 0, 0}, {VertexFormat::kVec4F32, 0, 1}}}}}); + transform->SetEntryPoint("main"); - auto result = manager()->Run(mod); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); + auto got = Transform(src, std::move(transform)); - EXPECT_EQ(R"(Module{ - TintVertexData Struct{ - [[block]] - StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4} - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - _tint_pulling_vertex_index - in - __i32 - } - Variable{ - Decorations{ - BindingDecoration{0} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_0 - storage_buffer - __struct_TintVertexData - } - Variable{ - var_a - private - __f32 - } - Variable{ - var_b - private - __array__f32_4 - } - Function main -> __void - StageDecoration{vertex} - () - { - Block{ - VariableDeclStatement{ - Variable{ - _tint_pulling_pos - function - __i32 - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_vertex_index} - multiply - ScalarConstructor[__u32]{16} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__f32]{var_a} - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - divide - ScalarConstructor[__u32]{4} - } - } - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_vertex_index} - multiply - ScalarConstructor[__u32]{16} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__array__f32_4]{var_b} - TypeConstructor[__vec_4__f32]{ - __vec_4__f32 - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{0} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{4} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{8} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{12} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } - } - } -} -)", - Demangler().Demangle(result.module, result.module.to_str())); + EXPECT_EQ(expect, got); } TEST_F(VertexPullingTest, FloatVectorAttributes) { - InitBasicModule(); - AddVertexInputVariable(0, "var_a", ty.array<f32, 2>()); - AddVertexInputVariable(1, "var_b", ty.array<f32, 3>()); - AddVertexInputVariable(2, "var_c", ty.array<f32, 4>()); + auto* src = R"( +[[location(0)]] var<in> var_a : array<f32, 2>; +[[location(1)]] var<in> var_b : array<f32, 3>; +[[location(2)]] var<in> var_c : array<f32, 4>; - InitTransform( +[[stage(vertex)]] +fn main() -> void {} +)"; + + auto* expect = R"( +[[block]] +struct TintVertexData { + [[offset(0)]] + _tint_vertex_data : [[stride(4)]] array<u32>; +}; + +[[builtin(vertex_idx)]] var<in> _tint_pulling_vertex_index : i32; +[[binding(0), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_0 : TintVertexData; +[[binding(1), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_1 : TintVertexData; +[[binding(2), set(4)]] var<storage_buffer> _tint_pulling_vertex_buffer_2 : TintVertexData; +var<private> var_a : array<f32, 2>; +var<private> var_b : array<f32, 3>; +var<private> var_c : array<f32, 4>; + +[[stage(vertex)]] +fn main() -> void { + { + var _tint_pulling_pos : i32; + _tint_pulling_pos = ((_tint_pulling_vertex_index * 8u) + 0u); + var_a = vec2<f32>(bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[((_tint_pulling_pos + 0u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_0._tint_vertex_data[((_tint_pulling_pos + 4u) / 4u)])); + _tint_pulling_pos = ((_tint_pulling_vertex_index * 12u) + 0u); + var_b = vec3<f32>(bitcast<f32>(_tint_pulling_vertex_buffer_1._tint_vertex_data[((_tint_pulling_pos + 0u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_1._tint_vertex_data[((_tint_pulling_pos + 4u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_1._tint_vertex_data[((_tint_pulling_pos + 8u) / 4u)])); + _tint_pulling_pos = ((_tint_pulling_vertex_index * 16u) + 0u); + var_c = vec4<f32>(bitcast<f32>(_tint_pulling_vertex_buffer_2._tint_vertex_data[((_tint_pulling_pos + 0u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_2._tint_vertex_data[((_tint_pulling_pos + 4u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_2._tint_vertex_data[((_tint_pulling_pos + 8u) / 4u)]), bitcast<f32>(_tint_pulling_vertex_buffer_2._tint_vertex_data[((_tint_pulling_pos + 12u) / 4u)])); + } +} +)"; + + auto transform = std::make_unique<VertexPulling>(); + transform->SetVertexState( {{{8, InputStepMode::kVertex, {{VertexFormat::kVec2F32, 0, 0}}}, {12, InputStepMode::kVertex, {{VertexFormat::kVec3F32, 0, 1}}}, {16, InputStepMode::kVertex, {{VertexFormat::kVec4F32, 0, 2}}}}}); + transform->SetEntryPoint("main"); - auto result = manager()->Run(mod); - ASSERT_FALSE(result.diagnostics.contains_errors()) - << diag::Formatter().format(result.diagnostics); + auto got = Transform(src, std::move(transform)); - EXPECT_EQ(R"(Module{ - TintVertexData Struct{ - [[block]] - StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4} - } - Variable{ - Decorations{ - BuiltinDecoration{vertex_idx} - } - _tint_pulling_vertex_index - in - __i32 - } - Variable{ - Decorations{ - BindingDecoration{0} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_0 - storage_buffer - __struct_TintVertexData - } - Variable{ - Decorations{ - BindingDecoration{1} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_1 - storage_buffer - __struct_TintVertexData - } - Variable{ - Decorations{ - BindingDecoration{2} - SetDecoration{4} - } - _tint_pulling_vertex_buffer_2 - storage_buffer - __struct_TintVertexData - } - Variable{ - var_a - private - __array__f32_2 - } - Variable{ - var_b - private - __array__f32_3 - } - Variable{ - var_c - private - __array__f32_4 - } - Function main -> __void - StageDecoration{vertex} - () - { - Block{ - VariableDeclStatement{ - Variable{ - _tint_pulling_pos - function - __i32 - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_vertex_index} - multiply - ScalarConstructor[__u32]{8} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__array__f32_2]{var_a} - TypeConstructor[__vec_2__f32]{ - __vec_2__f32 - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{0} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_0} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{4} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_vertex_index} - multiply - ScalarConstructor[__u32]{12} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__array__f32_3]{var_b} - TypeConstructor[__vec_3__f32]{ - __vec_3__f32 - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_1} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{0} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_1} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{4} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_1} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{8} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } - Assignment{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_in__i32]{_tint_pulling_vertex_index} - multiply - ScalarConstructor[__u32]{16} - } - add - ScalarConstructor[__u32]{0} - } - } - Assignment{ - Identifier[__ptr_private__array__f32_4]{var_c} - TypeConstructor[__vec_4__f32]{ - __vec_4__f32 - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_2} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{0} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_2} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{4} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_2} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{8} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - Bitcast[__f32]<__f32>{ - ArrayAccessor[__ptr_storage_buffer__u32]{ - MemberAccessor[__ptr_storage_buffer__array__u32_stride_4]{ - Identifier[__ptr_storage_buffer__struct_TintVertexData]{_tint_pulling_vertex_buffer_2} - Identifier[not set]{_tint_vertex_data} - } - Binary[__i32]{ - Binary[__i32]{ - Identifier[__ptr_function__i32]{_tint_pulling_pos} - add - ScalarConstructor[__u32]{12} - } - divide - ScalarConstructor[__u32]{4} - } - } - } - } - } - } - } -} -)", - Demangler().Demangle(result.module, result.module.to_str())); + EXPECT_EQ(expect, got); } } // namespace