[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.