[spirv-reader] Support unreachable, as a return

Bug: tint:3
Change-Id: Ia1384f84f7851a9e155c1536a624213ef51ee0a1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/22521
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 399ed3f..e3ef474 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -1772,10 +1772,24 @@
       // TODO(dneto): https://github.com/gpuweb/gpuweb/issues/676
       AddStatement(std::make_unique<ast::KillStatement>());
       return true;
+    case SpvOpUnreachable:
+      // Translate as if it's a return. This avoids the problem where WGSL
+      // requires a return statement at the end of the function body.
+      {
+        const auto* result_type = type_mgr_->GetType(function_.type_id());
+        if (result_type->AsVoid() != nullptr) {
+          AddStatement(std::make_unique<ast::ReturnStatement>());
+        } else {
+          auto* ast_type = parser_impl_.ConvertType(function_.type_id());
+          AddStatement(std::make_unique<ast::ReturnStatement>(
+              parser_impl_.MakeNullValue(ast_type)));
+        }
+      }
+      return true;
     default:
       break;
   }
-  // TODO(dneto): emit fallthrough, break, continue, kill
+  // TODO(dneto): emit fallthrough, break, continue
   return success();
 }
 
diff --git a/src/reader/spirv/function_cfg_test.cc b/src/reader/spirv/function_cfg_test.cc
index 1f05bbc..b9bc306 100644
--- a/src/reader/spirv/function_cfg_test.cc
+++ b/src/reader/spirv/function_cfg_test.cc
@@ -7947,6 +7947,121 @@
 )")) << ToString(fe.ast_body());
 }
 
+TEST_F(SpvParserTest, EmitBody_Unreachable_TopLevel) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpUnreachable
+
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+
+  EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Return{}
+)")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvParserTest, EmitBody_Unreachable_InsideIf) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpSelectionMerge %99 None
+     OpBranchConditional %cond %20 %99
+
+     %20 = OpLabel
+     OpUnreachable
+
+     %99 = OpLabel
+     OpReturn
+
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+
+  EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(If{
+  (
+    ScalarConstructor{false}
+  )
+  {
+    Return{}
+  }
+}
+Else{
+  {
+  }
+}
+Return{}
+)")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvParserTest, EmitBody_Unreachable_InsideLoop) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %80 None
+     OpBranchConditional %cond %30 %30
+
+     %30 = OpLabel
+     OpUnreachable
+
+     %80 = OpLabel
+     OpBranch %20
+
+     %99 = OpLabel
+     OpReturn
+
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+
+  EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Loop{
+  Return{}
+}
+Return{}
+)")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvParserTest, EmitBody_Unreachable_InNonVoidFunction) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %200 = OpFunction %uint None %uintfn
+
+     %210 = OpLabel
+     OpUnreachable
+
+     OpFunctionEnd
+
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     %11 = OpFunctionCall %uint %200
+     OpReturn
+
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(200));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+
+  EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Return{
+  {
+    ScalarConstructor{0}
+  }
+}
+)")) << ToString(fe.ast_body());
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace reader