[spirv-reader] Emit function variable initializers Bug: tint:3 Change-Id: I1c7fa2c282e6dd95ea048df83f608d4462150394 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/18820 Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 9950498..22dc974 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc
@@ -18,6 +18,11 @@ #include "source/opt/function.h" #include "source/opt/instruction.h" #include "source/opt/module.h" +#include "src/ast/bool_literal.h" +#include "src/ast/float_literal.h" +#include "src/ast/int_literal.h" +#include "src/ast/scalar_constructor_expression.h" +#include "src/ast/uint_literal.h" #include "src/ast/variable.h" #include "src/ast/variable_decl_statement.h" #include "src/reader/spirv/fail_stream.h" @@ -32,6 +37,8 @@ : parser_impl_(*pi), ast_module_(pi->get_module()), ir_context_(*(pi->ir_context())), + def_use_mgr_(ir_context_.get_def_use_mgr()), + constant_mgr_(ir_context_.get_constant_mgr()), type_mgr_(ir_context_.get_type_mgr()), fail_stream_(pi->fail_stream()), namer_(pi->namer()), @@ -142,6 +149,13 @@ auto var = parser_impl_.MakeVariable(inst.result_id(), ast::StorageClass::kNone, var_store_type); + if (inst.NumInOperands() > 1) { + // SPIR-V initializers are always constants. + // (OpenCL also allows the ID of an OpVariable, but we don't handle that + // here.) + var->set_constructor( + parser_impl_.MakeConstantExpression(inst.GetSingleWordInOperand(1))); + } // TODO(dneto): Add the initializer via Variable::set_constructor. auto var_decl_stmt = std::make_unique<ast::VariableDeclStatement>(std::move(var)); ast_body_.emplace_back(std::move(var_decl_stmt));
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h index 2a5c277..7a4dd95 100644 --- a/src/reader/spirv/function.h +++ b/src/reader/spirv/function.h
@@ -15,9 +15,11 @@ #ifndef SRC_READER_SPIRV_FUNCTION_H_ #define SRC_READER_SPIRV_FUNCTION_H_ +#include "source/opt/constants.h" #include "source/opt/function.h" #include "source/opt/ir_context.h" #include "source/opt/type_manager.h" +#include "src/ast/expression.h" #include "src/ast/module.h" #include "src/reader/spirv/fail_stream.h" #include "src/reader/spirv/namer.h" @@ -73,6 +75,8 @@ ParserImpl& parser_impl_; ast::Module& ast_module_; spvtools::opt::IRContext& ir_context_; + spvtools::opt::analysis::DefUseManager* def_use_mgr_; + spvtools::opt::analysis::ConstantManager* constant_mgr_; spvtools::opt::analysis::TypeManager* type_mgr_; FailStream& fail_stream_; Namer& namer_;
diff --git a/src/reader/spirv/function_var_test.cc b/src/reader/spirv/function_var_test.cc index e93bbaa..8dd8318 100644 --- a/src/reader/spirv/function_var_test.cc +++ b/src/reader/spirv/function_var_test.cc
@@ -43,13 +43,30 @@ return R"( %void = OpTypeVoid %voidfn = OpTypeFunction %void + + %bool = OpTypeBool %float = OpTypeFloat 32 %uint = OpTypeInt 32 0 %int = OpTypeInt 32 1 - %float_0 = OpConstant %float 0.0 + + %ptr_bool = OpTypePointer Function %bool %ptr_float = OpTypePointer Function %float %ptr_uint = OpTypePointer Function %uint %ptr_int = OpTypePointer Function %int + + %true = OpConstantTrue %bool + %false = OpConstantFalse %bool + %float_0 = OpConstant %float 0.0 + %float_1p5 = OpConstant %float 1.5 + %uint_1 = OpConstant %uint 1 + %int_m1 = OpConstant %int -1 + %uint_2 = OpConstant %uint 2 + + %v2float = OpTypeVector %float 2 + %m3v2float = OpTypeMatrix %v2float 3 + + %arr2uint = OpTypeArray %uint %uint_2 + %strct = OpTypeStruct %uint %float %arr2uint )"; } @@ -167,6 +184,296 @@ )")); } +TEST_F(SpvParserTest, EmitFunctionVariables_ScalarInitializers) { + auto p = parser( + test::Assemble(Names({"a", "b", "c", "d", "e"}) + CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %a = OpVariable %ptr_bool Function %true + %b = OpVariable %ptr_bool Function %false + %c = OpVariable %ptr_int Function %int_m1 + %d = OpVariable %ptr_uint Function %uint_1 + %e = OpVariable %ptr_float Function %float_1p5 + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + a + none + __bool + { + ScalarConstructor{true} + } + } +} +VariableDeclStatement{ + Variable{ + b + none + __bool + { + ScalarConstructor{false} + } + } +} +VariableDeclStatement{ + Variable{ + c + none + __i32 + { + ScalarConstructor{-1} + } + } +} +VariableDeclStatement{ + Variable{ + d + none + __u32 + { + ScalarConstructor{1} + } + } +} +VariableDeclStatement{ + Variable{ + e + none + __f32 + { + ScalarConstructor{1.500000} + } + } +} +)")); +} + +TEST_F(SpvParserTest, EmitFunctionVariables_ScalarNullInitializers) { + auto p = + parser(test::Assemble(Names({"a", "b", "c", "d"}) + CommonTypes() + R"( + %null_bool = OpConstantNull %bool + %null_int = OpConstantNull %int + %null_uint = OpConstantNull %uint + %null_float = OpConstantNull %float + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %a = OpVariable %ptr_bool Function %null_bool + %b = OpVariable %ptr_int Function %null_int + %c = OpVariable %ptr_uint Function %null_uint + %d = OpVariable %ptr_float Function %null_float + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error(); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + a + none + __bool + { + ScalarConstructor{false} + } + } +} +VariableDeclStatement{ + Variable{ + b + none + __i32 + { + ScalarConstructor{0} + } + } +} +VariableDeclStatement{ + Variable{ + c + none + __u32 + { + ScalarConstructor{0} + } + } +} +VariableDeclStatement{ + Variable{ + d + none + __f32 + { + ScalarConstructor{0.000000} + } + } +} +)")); +} + +TEST_F(SpvParserTest, EmitFunctionVariables_VectorInitializer) { + auto p = parser(test::Assemble(CommonTypes() + R"( + %ptr = OpTypePointer Function %v2float + %two = OpConstant %float 2.0 + %const = OpConstantComposite %v2float %float_1p5 %two + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %200 = OpVariable %ptr Function %const + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_200 + none + __vec_2__f32 + { + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{1.500000} + ScalarConstructor{2.000000} + } + } + } +} +)")) << ToString(fe.ast_body()); +} + +TEST_F(SpvParserTest, EmitFunctionVariables_MatrixInitializer) { + auto p = parser(test::Assemble(CommonTypes() + R"( + %ptr = OpTypePointer Function %m3v2float + %two = OpConstant %float 2.0 + %three = OpConstant %float 3.0 + %four = OpConstant %float 4.0 + %v0 = OpConstantComposite %v2float %float_1p5 %two + %v1 = OpConstantComposite %v2float %two %three + %v2 = OpConstantComposite %v2float %three %four + %const = OpConstantComposite %m3v2float %v0 %v1 %v2 + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %200 = OpVariable %ptr Function %const + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_200 + none + __mat_2_3__f32 + { + TypeConstructor{ + __mat_2_3__f32 + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{1.500000} + ScalarConstructor{2.000000} + } + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{2.000000} + ScalarConstructor{3.000000} + } + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{3.000000} + ScalarConstructor{4.000000} + } + } + } + } +} +)")); +} + +TEST_F(SpvParserTest, EmitFunctionVariables_ArrayInitializer) { + auto p = parser(test::Assemble(CommonTypes() + R"( + %ptr = OpTypePointer Function %arr2uint + %two = OpConstant %uint 2 + %const = OpConstantComposite %arr2uint %uint_1 %two + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %200 = OpVariable %ptr Function %const + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_200 + none + __array__u32_2 + { + TypeConstructor{ + __array__u32_2 + ScalarConstructor{1} + ScalarConstructor{2} + } + } + } +} +)")); +} + +TEST_F(SpvParserTest, EmitFunctionVariables_StructInitializer) { + auto p = parser(test::Assemble(CommonTypes() + R"( + %ptr = OpTypePointer Function %strct + %two = OpConstant %uint 2 + %arrconst = OpConstantComposite %arr2uint %uint_1 %two + %const = OpConstantComposite %strct %uint_1 %float_1p5 %arrconst + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %200 = OpVariable %ptr Function %const + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_200 + none + __struct_S + { + TypeConstructor{ + __struct_S + ScalarConstructor{1} + ScalarConstructor{1.500000} + TypeConstructor{ + __array__u32_2 + ScalarConstructor{1} + ScalarConstructor{2} + } + } + } + } +} +)")); +} + } // namespace } // namespace spirv } // namespace reader
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc index fc00865..4eb5db3 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc
@@ -25,6 +25,7 @@ #include "source/opt/basic_block.h" #include "source/opt/build_module.h" +#include "source/opt/constants.h" #include "source/opt/decoration_manager.h" #include "source/opt/function.h" #include "source/opt/instruction.h" @@ -32,8 +33,12 @@ #include "source/opt/type_manager.h" #include "source/opt/types.h" #include "spirv-tools/libspirv.hpp" +#include "src/ast/bool_literal.h" #include "src/ast/builtin_decoration.h" #include "src/ast/decorated_variable.h" +#include "src/ast/float_literal.h" +#include "src/ast/int_literal.h" +#include "src/ast/scalar_constructor_expression.h" #include "src/ast/struct.h" #include "src/ast/struct_decoration.h" #include "src/ast/struct_member.h" @@ -51,6 +56,8 @@ #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/ast/variable_decoration.h" @@ -750,6 +757,80 @@ return ast_var; } +std::unique_ptr<ast::Expression> ParserImpl::MakeConstantExpression( + uint32_t id) { + if (!success_) { + return nullptr; + } + const auto* inst = def_use_mgr_->GetDef(id); + if (inst == nullptr) { + Fail() << "ID " << id << " is not a registered instruction"; + return nullptr; + } + auto* ast_type = ConvertType(inst->type_id()); + if (ast_type == nullptr) { + return nullptr; + } + // TODO(dneto): Handle spec constants too? + const auto* spirv_const = constant_mgr_->FindDeclaredConstant(id); + if (spirv_const == nullptr) { + Fail() << "ID " << id << " is not a constant"; + return nullptr; + } + // TODO(dneto): Note: NullConstant for int, uint, float map to a regular 0. + // So canonicalization should map that way too. + // Currently "null<type>" is missing from the WGSL parser. + // See https://bugs.chromium.org/p/tint/issues/detail?id=34 + if (ast_type->IsU32()) { + return std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::UintLiteral>(ast_type, spirv_const->GetU32())); + } + if (ast_type->IsI32()) { + return std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::IntLiteral>(ast_type, spirv_const->GetS32())); + } + if (ast_type->IsF32()) { + return std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::FloatLiteral>(ast_type, spirv_const->GetFloat())); + } + if (ast_type->IsBool()) { + const bool value = spirv_const->AsNullConstant() ? false : + spirv_const->AsBoolConstant()->value(); + return std::make_unique<ast::ScalarConstructorExpression>( + std::make_unique<ast::BoolLiteral>( + ast_type, value)); + } + auto spirv_composite_const = spirv_const->AsCompositeConstant(); + if (spirv_composite_const != nullptr) { + // Handle vector, matrix, array, and struct + + // TODO(dneto): Handle the spirv_composite_const->IsZero() case specially. + // See https://github.com/gpuweb/gpuweb/issues/685 + + // Generate a composite from explicit components. + ast::ExpressionList ast_components; + for (const auto* component : spirv_composite_const->GetComponents()) { + auto* def = constant_mgr_->GetDefiningInstruction(component); + if (def == nullptr) { + Fail() << "internal error: SPIR-V constant doesn't have defining " + "instruction"; + return nullptr; + } + auto ast_component = MakeConstantExpression(def->result_id()); + if (!success_) { + // We've already emitted a diagnostic. + return nullptr; + } + ast_components.emplace_back(std::move(ast_component)); + } + return std::make_unique<ast::TypeConstructorExpression>( + ast_type, std::move(ast_components)); + } + Fail() << "Unhandled constant type " << inst->type_id() << " for value ID " + << id; + return nullptr; +} + bool ParserImpl::EmitFunctions() { if (!success_) { return false;
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h index dc42e75..8edc327 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h
@@ -221,6 +221,11 @@ ast::StorageClass sc, ast::type::Type* type); + /// Creates an AST expression node for a SPIR-V constant. + /// @param id the SPIR-V ID of the constant + /// @returns a new Literal node + std::unique_ptr<ast::Expression> MakeConstantExpression(uint32_t id); + private: /// Converts a specific SPIR-V type to a Tint type. Integer case ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);