[spirv-reader] First GLSL.std.450 instructions
This supports the extended instructions used by the compute_boids
example.
Bug: tint:3
Change-Id: I364c343217139e489377dd2a9330058114023caa
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/23126
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 4009366..9dbeb92 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -646,6 +646,7 @@
"src/reader/spirv/function_cfg_test.cc",
"src/reader/spirv/function_conversion_test.cc",
"src/reader/spirv/function_decl_test.cc",
+ "src/reader/spirv/function_glsl_std_450_test.cc",
"src/reader/spirv/function_logical_test.cc",
"src/reader/spirv/function_memory_test.cc",
"src/reader/spirv/function_var_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 01e8a00..55a24a6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -322,6 +322,7 @@
reader/spirv/function_cfg_test.cc
reader/spirv/function_conversion_test.cc
reader/spirv/function_decl_test.cc
+ reader/spirv/function_glsl_std_450_test.cc
reader/spirv/function_logical_test.cc
reader/spirv/function_var_test.cc
reader/spirv/function_memory_test.cc
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index a5cefcb..f9aca7e 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -24,11 +24,13 @@
#include "source/opt/function.h"
#include "source/opt/instruction.h"
#include "source/opt/module.h"
+#include "spirv/unified1/GLSL.std.450.h"
#include "src/ast/array_accessor_expression.h"
#include "src/ast/as_expression.h"
#include "src/ast/assignment_statement.h"
#include "src/ast/binary_expression.h"
#include "src/ast/break_statement.h"
+#include "src/ast/call_expression.h"
#include "src/ast/case_statement.h"
#include "src/ast/continue_statement.h"
#include "src/ast/else_statement.h"
@@ -271,6 +273,32 @@
return ast::BinaryOp::kNone;
}
+// Returns the WGSL standard library function for the given
+// GLSL.std.450 extended instruction operation code. Unknown
+// and invalid opcodes map to the empty string.
+// @returns the WGSL standard function name, or an empty string.
+std::string GetGlslStd450FuncName(uint32_t ext_opcode) {
+ switch (ext_opcode) {
+ case GLSLstd450Atan2:
+ return "atan2";
+ case GLSLstd450Cos:
+ return "cos";
+ case GLSLstd450Sin:
+ return "sin";
+ case GLSLstd450Distance:
+ return "distance";
+ case GLSLstd450Normalize:
+ return "normalize";
+ case GLSLstd450FClamp:
+ return "fclamp";
+ case GLSLstd450Length:
+ return "length";
+ default:
+ break;
+ }
+ return "";
+}
+
// @returns the merge block ID for the given basic block, or 0 if there is none.
uint32_t MergeFor(const spvtools::opt::BasicBlock& bb) {
// Get the OpSelectionMerge or OpLoopMerge instruction, if any.
@@ -2353,6 +2381,15 @@
return {ast_type, std::move(negated_expr)};
}
+ if (opcode == SpvOpExtInst) {
+ const auto import = inst.GetSingleWordInOperand(0);
+ if (parser_impl_.glsl_std_450_imports().count(import) == 0) {
+ Fail() << "unhandled extended instruction import with ID " << import;
+ return {};
+ }
+ return EmitGlslStd450ExtInst(inst);
+ }
+
// builtin readonly function
// glsl.std.450 readonly function
@@ -2384,6 +2421,27 @@
return {};
}
+TypedExpression FunctionEmitter::EmitGlslStd450ExtInst(
+ const spvtools::opt::Instruction& inst) {
+ const auto ext_opcode = inst.GetSingleWordInOperand(1);
+ const auto name = GetGlslStd450FuncName(ext_opcode);
+ if (name.empty()) {
+ Fail() << "unhandled GLSL.std.450 instruction " << ext_opcode;
+ return {};
+ }
+ auto func = std::make_unique<ast::IdentifierExpression>(
+ std::vector<std::string>{parser_impl_.GlslStd450Prefix(), name});
+ ast::ExpressionList operands;
+ // All parameters to GLSL.std.450 extended instructions are IDs.
+ for (uint32_t iarg = 2; iarg < inst.NumInOperands(); ++iarg) {
+ operands.emplace_back(MakeOperand(inst, iarg).expr);
+ }
+ auto* ast_type = parser_impl_.ConvertType(inst.type_id());
+ auto call = std::make_unique<ast::CallExpression>(std::move(func),
+ std::move(operands));
+ return {ast_type, std::move(call)};
+}
+
TypedExpression FunctionEmitter::MakeAccessChain(
const spvtools::opt::Instruction& inst) {
if (inst.NumInOperands() < 1) {
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index 1719def..0e92907 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -427,6 +427,12 @@
TypedExpression MaybeEmitCombinatorialValue(
const spvtools::opt::Instruction& inst);
+ /// Creates an expression and supporting statements for the a GLSL.std.450
+ /// extended instruction.
+ /// @param inst a SPIR-V OpExtInst instruction from GLSL.std.450
+ /// @returns an AST expression for the instruction, or nullptr.
+ TypedExpression EmitGlslStd450ExtInst(const spvtools::opt::Instruction& inst);
+
/// Gets the block info for a block ID, if any exists
/// @param id the SPIR-V ID of the OpLabel instruction starting the block
/// @returns the block info for the given ID, if it exists, or nullptr
diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc
new file mode 100644
index 0000000..a73187e
--- /dev/null
+++ b/src/reader/spirv/function_glsl_std_450_test.cc
@@ -0,0 +1,465 @@
+// Copyright 2020 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.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "src/reader/spirv/fail_stream.h"
+#include "src/reader/spirv/function.h"
+#include "src/reader/spirv/parser_impl.h"
+#include "src/reader/spirv/parser_impl_test_helper.h"
+#include "src/reader/spirv/spirv_tools_helpers_test.h"
+
+namespace tint {
+namespace reader {
+namespace spirv {
+namespace {
+
+using ::testing::HasSubstr;
+
+std::string Preamble() {
+ return R"(
+ OpCapability Shader
+ %glsl = OpExtInstImport "GLSL.std.450"
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+
+ %uint = OpTypeInt 32 0
+ %int = OpTypeInt 32 1
+ %float = OpTypeFloat 32
+
+ %uint_10 = OpConstant %uint 10
+ %uint_20 = OpConstant %uint 20
+ %int_30 = OpConstant %int 30
+ %int_40 = OpConstant %int 40
+ %float_50 = OpConstant %float 50
+ %float_60 = OpConstant %float 60
+ %float_70 = OpConstant %float 70
+
+ %v2uint = OpTypeVector %uint 2
+ %v2int = OpTypeVector %int 2
+ %v2float = OpTypeVector %float 2
+
+ %v2uint_10_20 = OpConstantComposite %v2uint %uint_10 %uint_20
+ %v2uint_20_10 = OpConstantComposite %v2uint %uint_20 %uint_10
+ %v2int_30_40 = OpConstantComposite %v2int %int_30 %int_40
+ %v2int_40_30 = OpConstantComposite %v2int %int_40 %int_30
+ %v2float_50_60 = OpConstantComposite %v2float %float_50 %float_60
+ %v2float_60_50 = OpConstantComposite %v2float %float_60 %float_50
+ %v2float_70_70 = OpConstantComposite %v2float %float_70 %float_70
+)";
+}
+
+struct GlslStd450Case {
+ std::string opcode;
+ std::string wgsl_func;
+};
+inline std::ostream& operator<<(std::ostream& out, GlslStd450Case c) {
+ out << "GlslStd450Case(" << c.opcode << " " << c.wgsl_func << ")";
+ return out;
+}
+
+// Nomenclature:
+// Float = scalar float
+// Floating = scalar float or vector-of-float
+
+using SpvParserTest_GlslStd450_Float_Floating =
+ SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
+using SpvParserTest_GlslStd450_Float_FloatingFloating =
+ SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
+using SpvParserTest_GlslStd450_Floating_Floating =
+ SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
+using SpvParserTest_GlslStd450_Floating_FloatingFloating =
+ SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
+using SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating =
+ SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
+
+TEST_P(SpvParserTest_GlslStd450_Float_Floating, Scalar) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %float %glsl )" +
+ GetParam().opcode + R"( %float_50
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ ScalarConstructor{50.000000}
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Float_Floating, Vector) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %float %glsl )" +
+ GetParam().opcode + R"( %v2float_50_60
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ }
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Float_FloatingFloating, Scalar) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %float %glsl )" +
+ GetParam().opcode + R"( %float_50 %float_60
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Float_FloatingFloating, Vector) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %float %glsl )" +
+ GetParam().opcode + R"( %v2float_50_60 %v2float_60_50
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ }
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{60.000000}
+ ScalarConstructor{50.000000}
+ }
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Floating_Floating, Scalar) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %float %glsl )" +
+ GetParam().opcode + R"( %float_50
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ ScalarConstructor{50.000000}
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Floating_Floating, Vector) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %v2float %glsl )" +
+ GetParam().opcode + R"( %v2float_50_60
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __vec_2__f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ }
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloating, Scalar) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %float %glsl )" +
+ GetParam().opcode + R"( %float_50 %float_60
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloating, Vector) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %v2float %glsl )" +
+ GetParam().opcode + R"( %v2float_50_60 %v2float_60_50
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __vec_2__f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ }
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{60.000000}
+ ScalarConstructor{50.000000}
+ }
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating, Scalar) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %float %glsl )" +
+ GetParam().opcode + R"( %float_50 %float_60 %float_70
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ ScalarConstructor{70.000000}
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating, Vector) {
+ const auto assembly = Preamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+ %1 = OpExtInst %v2float %glsl )" +
+ GetParam().opcode +
+ R"( %v2float_50_60 %v2float_60_50 %v2float_70_70
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ FunctionEmitter fe(p, *spirv_function(100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
+ Variable{
+ x_1
+ none
+ __vec_2__f32
+ {
+ Call{
+ Identifier{)" + GetParam().wgsl_func + R"(}
+ (
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{50.000000}
+ ScalarConstructor{60.000000}
+ }
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{60.000000}
+ ScalarConstructor{50.000000}
+ }
+ TypeConstructor{
+ __vec_2__f32
+ ScalarConstructor{70.000000}
+ ScalarConstructor{70.000000}
+ }
+ )
+ }
+ }
+ })"))
+ << ToString(fe.ast_body());
+}
+
+INSTANTIATE_TEST_SUITE_P(Samples,
+ SpvParserTest_GlslStd450_Float_Floating,
+ ::testing::Values(GlslStd450Case{
+ "Length", "std::glsl::length"}));
+
+INSTANTIATE_TEST_SUITE_P(Samples,
+ SpvParserTest_GlslStd450_Float_FloatingFloating,
+ ::testing::Values(GlslStd450Case{
+ "Distance", "std::glsl::distance"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ Samples,
+ SpvParserTest_GlslStd450_Floating_Floating,
+ ::testing::Values(GlslStd450Case{"Sin", "std::glsl::sin"},
+ GlslStd450Case{"Cos", "std::glsl::cos"},
+ GlslStd450Case{"Normalize", "std::glsl::normalize"}));
+
+INSTANTIATE_TEST_SUITE_P(Samples,
+ SpvParserTest_GlslStd450_Floating_FloatingFloating,
+ ::testing::Values(GlslStd450Case{"Atan2",
+ "std::glsl::atan2"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ Samples,
+ SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating,
+ ::testing::Values(GlslStd450Case{"FClamp", "std::glsl::fclamp"}));
+
+} // namespace
+} // namespace spirv
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 623ac51..f778c61 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -454,7 +454,7 @@
// This is a canonicalization.
if (glsl_std_450_imports_.empty()) {
auto ast_import =
- std::make_unique<tint::ast::Import>(name, "std::glsl");
+ std::make_unique<tint::ast::Import>(name, GlslStd450Prefix());
import_map_[import.result_id()] = ast_import.get();
ast_module_.AddImport(std::move(ast_import));
}
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index d99b73c..f7330a8 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -128,6 +128,9 @@
return glsl_std_450_imports_;
}
+ /// @returns the import prefix to use for the GLSL.std.450 import.
+ std::string GlslStd450Prefix() const { return "std::glsl"; }
+
/// Converts a SPIR-V type to a Tint type, and saves it for fast lookup.
/// On failure, logs an error and returns null. This should only be called
/// after the internal representation of the module has been built.