[spirv-reader] Handle entry point declarations for GLCompute

Emit @compute and @workgroup_size attributes, and set the entry point
function name.

Bug: tint:1907
Change-Id: I800ac767b9b1798cf3cc85f67cc3d6bc07edb785
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/165560
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/spirv/reader/parser/function_test.cc b/src/tint/lang/spirv/reader/parser/function_test.cc
index be6c28c..66e7ae3 100644
--- a/src/tint/lang/spirv/reader/parser/function_test.cc
+++ b/src/tint/lang/spirv/reader/parser/function_test.cc
@@ -44,7 +44,7 @@
                OpFunctionEnd
 )",
               R"(
-%1 = func():void -> %b1 {
+%main = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
   %b1 = block {
     ret
   }
@@ -52,5 +52,62 @@
 )");
 }
 
+TEST_F(SpirvParserTest, LocalSize) {
+    EXPECT_IR(R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 3 4 5
+       %void = OpTypeVoid
+    %ep_type = OpTypeFunction %void
+       %main = OpFunction %void None %ep_type
+ %main_start = OpLabel
+               OpReturn
+               OpFunctionEnd
+)",
+              R"(
+%main = @compute @workgroup_size(3, 4, 5) func():void -> %b1 {
+  %b1 = block {
+    ret
+  }
+}
+)");
+}
+
+TEST_F(SpirvParserTest, MultipleEntryPoints) {
+    EXPECT_IR(R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %foo "foo"
+               OpEntryPoint GLCompute %bar "bar"
+               OpExecutionMode %foo LocalSize 3 4 5
+               OpExecutionMode %bar LocalSize 6 7 8
+       %void = OpTypeVoid
+    %ep_type = OpTypeFunction %void
+
+        %foo = OpFunction %void None %ep_type
+  %foo_start = OpLabel
+               OpReturn
+               OpFunctionEnd
+
+        %bar = OpFunction %void None %ep_type
+  %bar_start = OpLabel
+               OpReturn
+               OpFunctionEnd
+)",
+              R"(
+%foo = @compute @workgroup_size(3, 4, 5) func():void -> %b1 {
+  %b1 = block {
+    ret
+  }
+}
+%bar = @compute @workgroup_size(6, 7, 8) func():void -> %b2 {
+  %b2 = block {
+    ret
+  }
+}
+)");
+}
+
 }  // namespace
 }  // namespace tint::spirv::reader
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index aacba85..1559ab5 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -77,8 +77,8 @@
 
         EmitFunctions();
 
-        // TODO(crbug.com/tint/1907): Handle entry point declarations and execution modes.
-        // TODO(crbug.com/tint/1907): Handle entry point declarations and execution modes.
+        EmitEntryPoints();
+
         // TODO(crbug.com/tint/1907): Handle annotation instructions.
         // TODO(crbug.com/tint/1907): Handle names.
 
@@ -109,10 +109,50 @@
             // TODO(crbug.com/tint/1907): Emit function parameters as well.
             current_function_ = b_.Function(
                 Type(func.type_id()), core::ir::Function::PipelineStage::kUndefined, std::nullopt);
+            functions_.Add(func.result_id(), current_function_);
             EmitBlock(current_function_->Block(), *func.entry());
         }
     }
 
+    /// Emit entry point attributes.
+    void EmitEntryPoints() {
+        // Handle OpEntryPoint declarations.
+        for (auto& entry_point : spirv_context_->module()->entry_points()) {
+            auto model = entry_point.GetSingleWordInOperand(0);
+            auto* func = functions_.Get(entry_point.GetSingleWordInOperand(1)).value_or(nullptr);
+            TINT_ASSERT_OR_RETURN(func);
+
+            // Set the pipeline stage.
+            switch (spv::ExecutionModel(model)) {
+                case spv::ExecutionModel::GLCompute:
+                    func->SetStage(core::ir::Function::PipelineStage::kCompute);
+                    break;
+                default:
+                    TINT_UNIMPLEMENTED() << "unhandled execution model: " << model;
+            }
+
+            // Set the entry point name.
+            ir_.SetName(func, entry_point.GetOperand(2).AsString());
+        }
+
+        // Handle OpExecutionMode declarations.
+        for (auto& execution_mode : spirv_context_->module()->execution_modes()) {
+            auto* func = functions_.Get(execution_mode.GetSingleWordInOperand(0)).value_or(nullptr);
+            auto mode = execution_mode.GetSingleWordInOperand(1);
+            TINT_ASSERT_OR_RETURN(func);
+
+            switch (spv::ExecutionMode(mode)) {
+                case spv::ExecutionMode::LocalSize:
+                    func->SetWorkgroupSize(execution_mode.GetSingleWordInOperand(2),
+                                           execution_mode.GetSingleWordInOperand(3),
+                                           execution_mode.GetSingleWordInOperand(4));
+                    break;
+                default:
+                    TINT_UNIMPLEMENTED() << "unhandled execution mode: " << mode;
+            }
+        }
+    }
+
     /// Emit the contents of SPIR-V block @p src into Tint IR block @p dst.
     /// @param dst the Tint IR block to append to
     /// @param src the SPIR-V block to emit
@@ -139,6 +179,8 @@
 
     /// The Tint IR function that is currently being emitted.
     core::ir::Function* current_function_ = nullptr;
+    /// A map from a SPIR-V function definition result ID to the corresponding Tint function object.
+    Hashmap<uint32_t, core::ir::Function*, 8> functions_;
 
     /// The SPIR-V context containing the SPIR-V tools intermediate representation.
     std::unique_ptr<spvtools::opt::IRContext> spirv_context_;