Generate OpExtInstImport

This CL updates the SPIR-V generator to only create the GLSL import if
it's requested.

Bug: tint:5
Change-Id: I96a9100adf0a0c59dcdd82c12ac27c566ea2663f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17341
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 4c6f0bd..57df564 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -35,10 +35,15 @@
 
 Builder::~Builder() = default;
 
-bool Builder::Build(const ast::Module&) {
+bool Builder::Build(const ast::Module& m) {
   push_preamble(spv::Op::OpCapability, {Operand::Int(SpvCapabilityShader)});
-  push_preamble(spv::Op::OpExtInstImport,
-                {result_op(), Operand::String("GLSL.std.450")});
+  push_preamble(spv::Op::OpCapability,
+                {Operand::Int(SpvCapabilityVulkanMemoryModel)});
+
+  for (const auto& imp : m.imports()) {
+    GenerateImport(imp.get());
+  }
+
   push_preamble(spv::Op::OpMemoryModel,
                 {Operand::Int(SpvAddressingModelLogical),
                  Operand::Int(SpvMemoryModelVulkanKHR)});
@@ -80,6 +85,15 @@
   }
 }
 
+void Builder::GenerateImport(ast::Import* imp) {
+  auto op = result_op();
+  auto id = op.to_i();
+
+  push_preamble(spv::Op::OpExtInstImport, {op, Operand::String(imp->path())});
+
+  import_name_to_id_[imp->name()] = id;
+}
+
 }  // namespace spirv
 }  // namespace writer
 }  // namespace tint
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 7b27b18..0788621 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -16,6 +16,8 @@
 #define SRC_WRITER_SPIRV_BUILDER_H_
 
 #include <functional>
+#include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "src/ast/module.h"
@@ -43,6 +45,13 @@
   /// @returns the id bound for this module
   uint32_t id_bound() const { return next_id_; }
 
+  /// @returns the next id to be used
+  uint32_t next_id() {
+    auto id = next_id_;
+    next_id_ += 1;
+    return id;
+  }
+
   /// Iterates over all the instructions in the correct order and calls the
   /// given callback
   /// @param cb the callback to execute
@@ -89,13 +98,12 @@
   /// @returns the annotations
   const std::vector<Instruction>& annot() const { return annotations_; }
 
+  /// Generates an import instruction
+  /// @param imp the import
+  void GenerateImport(ast::Import* imp);
+
  private:
   Operand result_op();
-  uint32_t next_id() {
-    auto id = next_id_;
-    next_id_ += 1;
-    return id;
-  }
 
   uint32_t next_id_ = 1;
   std::vector<Instruction> preamble_;
@@ -103,6 +111,8 @@
   std::vector<Instruction> types_;
   std::vector<Instruction> instructions_;
   std::vector<Instruction> annotations_;
+
+  std::unordered_map<std::string, uint32_t> import_name_to_id_;
 };
 
 }  // namespace spirv
diff --git a/src/writer/spirv/builder_test.cc b/src/writer/spirv/builder_test.cc
index 5e44c73..8d06910 100644
--- a/src/writer/spirv/builder_test.cc
+++ b/src/writer/spirv/builder_test.cc
@@ -14,8 +14,12 @@
 
 #include "src/writer/spirv/builder.h"
 
+#include <memory>
+
 #include "gtest/gtest.h"
+#include "spirv/unified1/spirv.h"
 #include "spirv/unified1/spirv.hpp11"
+#include "src/ast/import.h"
 #include "src/ast/module.h"
 
 namespace tint {
@@ -24,17 +28,48 @@
 
 using BuilderTest = testing::Test;
 
-TEST_F(BuilderTest, InsertsPreamble) {
+TEST_F(BuilderTest, InsertsPreambleWithImport) {
+  ast::Module m;
+  m.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "glsl"));
+
+  Builder b;
+  ASSERT_TRUE(b.Build(m));
+  ASSERT_EQ(b.preamble().size(), 4);
+
+  auto pre = b.preamble();
+  EXPECT_EQ(pre[0].opcode(), spv::Op::OpCapability);
+  EXPECT_EQ(pre[0].operands()[0].to_i(), SpvCapabilityShader);
+  EXPECT_EQ(pre[1].opcode(), spv::Op::OpCapability);
+  EXPECT_EQ(pre[1].operands()[0].to_i(), SpvCapabilityVulkanMemoryModel);
+  EXPECT_EQ(pre[2].opcode(), spv::Op::OpExtInstImport);
+  EXPECT_EQ(pre[2].operands()[1].to_s(), "GLSL.std.450");
+  EXPECT_EQ(pre[3].opcode(), spv::Op::OpMemoryModel);
+}
+
+TEST_F(BuilderTest, InsertsPreambleWithoutImport) {
   ast::Module m;
   Builder b;
   ASSERT_TRUE(b.Build(m));
   ASSERT_EQ(b.preamble().size(), 3);
+
   auto pre = b.preamble();
   EXPECT_EQ(pre[0].opcode(), spv::Op::OpCapability);
-  EXPECT_EQ(pre[1].opcode(), spv::Op::OpExtInstImport);
+  EXPECT_EQ(pre[0].operands()[0].to_i(), SpvCapabilityShader);
+  EXPECT_EQ(pre[1].opcode(), spv::Op::OpCapability);
+  EXPECT_EQ(pre[1].operands()[0].to_i(), SpvCapabilityVulkanMemoryModel);
   EXPECT_EQ(pre[2].opcode(), spv::Op::OpMemoryModel);
 }
 
+TEST_F(BuilderTest, TracksIdBounds) {
+  Builder b;
+
+  for (size_t i = 0; i < 5; i++) {
+    EXPECT_EQ(b.next_id(), i + 1);
+  }
+
+  EXPECT_EQ(6, b.id_bound());
+}
+
 }  // namespace spirv
 }  // namespace writer
 }  // namespace tint