Update spirv-reader tests to be valid for WebGPU

- each vertex shader must have builtin-position output.
- when testing non-vertex shader features, change to fragment shaders,
  to avoid the need to have a builtin position output.
- Fix ordering of entry point declaration vs. OpName

Bug: tint:1043
Change-Id: Ic66383d42419a1ef893835ae71729ae27fc6e539
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/59482
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: David Neto <dneto@google.com>
diff --git a/src/reader/spirv/function_bit_test.cc b/src/reader/spirv/function_bit_test.cc
index 7ee6705..b8bccf3 100644
--- a/src/reader/spirv/function_bit_test.cc
+++ b/src/reader/spirv/function_bit_test.cc
@@ -60,7 +60,8 @@
   return R"(
   OpCapability Shader
   OpMemoryModel Logical Simple
-  OpEntryPoint Vertex %100 "main"
+  OpEntryPoint Fragment %100 "main"
+  OpExecutionMode %100 OriginUpperLeft
 )" + CommonTypes();
 }
 
diff --git a/src/reader/spirv/function_call_test.cc b/src/reader/spirv/function_call_test.cc
index 007707a..3060d20 100644
--- a/src/reader/spirv/function_call_test.cc
+++ b/src/reader/spirv/function_call_test.cc
@@ -29,7 +29,8 @@
   return R"(
      OpCapability Shader
      OpMemoryModel Logical Simple
-     OpEntryPoint Vertex %100 "x_100"
+     OpEntryPoint Fragment %100 "x_100"
+     OpExecutionMode %100 OriginUpperLeft
 )";
 }
 
@@ -68,7 +69,7 @@
     Return{}
   }
   Function $3 -> __void
-  StageDecoration{vertex}
+  StageDecoration{fragment}
   ()
   {
     Call[not set]{
@@ -286,7 +287,7 @@
     Return{}
   }
   Function x_100 -> __void
-  StageDecoration{vertex}
+  StageDecoration{fragment}
   ()
   {
     Call[not set]{
diff --git a/src/reader/spirv/function_composite_test.cc b/src/reader/spirv/function_composite_test.cc
index 1f4acf3..3337686 100644
--- a/src/reader/spirv/function_composite_test.cc
+++ b/src/reader/spirv/function_composite_test.cc
@@ -469,7 +469,8 @@
   const std::string assembly = R"(
      OpCapability Shader
      OpMemoryModel Logical Simple
-     OpEntryPoint Vertex %100 "main"
+     OpEntryPoint Fragment %100 "main"
+     OpExecutionMode %100 OriginUpperLeft
 
      OpMemberName %s0 0 "algo"
      OpMemberName %s1 0 "rithm"
@@ -927,7 +928,8 @@
   const std::string assembly = R"(
      OpCapability Shader
      OpMemoryModel Logical Simple
-     OpEntryPoint Vertex %100 "main"
+     OpEntryPoint Fragment %100 "main"
+     OpExecutionMode %100 OriginUpperLeft
 
      OpName %var0 "var0"
      OpName %var1 "var1"
diff --git a/src/reader/spirv/function_conversion_test.cc b/src/reader/spirv/function_conversion_test.cc
index cb3d381..cdf4ff2 100644
--- a/src/reader/spirv/function_conversion_test.cc
+++ b/src/reader/spirv/function_conversion_test.cc
@@ -29,7 +29,8 @@
   return R"(
   OpCapability Shader
   OpMemoryModel Logical Simple
-  OpEntryPoint Vertex %100 "main"
+  OpEntryPoint Fragment %100 "main"
+  OpExecutionMode %100 OriginUpperLeft
 
   %void = OpTypeVoid
   %voidfn = OpTypeFunction %void
diff --git a/src/reader/spirv/function_decl_test.cc b/src/reader/spirv/function_decl_test.cc
index aec687b..4380843 100644
--- a/src/reader/spirv/function_decl_test.cc
+++ b/src/reader/spirv/function_decl_test.cc
@@ -28,7 +28,8 @@
   return R"(
     OpCapability Shader
     OpMemoryModel Logical Simple
-    OpEntryPoint Vertex %100 "x_100"
+    OpEntryPoint Fragment %100 "x_100"
+    OpExecutionMode %100 OriginUpperLeft
   )";
 }
 
diff --git a/src/reader/spirv/function_logical_test.cc b/src/reader/spirv/function_logical_test.cc
index 2d21a1b..173a17e 100644
--- a/src/reader/spirv/function_logical_test.cc
+++ b/src/reader/spirv/function_logical_test.cc
@@ -28,7 +28,8 @@
   return R"(
   OpCapability Shader
   OpMemoryModel Logical Simple
-  OpEntryPoint Vertex %100 "main"
+  OpEntryPoint Fragment %100 "main"
+  OpExecutionMode %100 OriginUpperLeft
 
   %void = OpTypeVoid
   %voidfn = OpTypeFunction %void
diff --git a/src/reader/spirv/function_memory_test.cc b/src/reader/spirv/function_memory_test.cc
index 5249114..22c95c6 100644
--- a/src/reader/spirv/function_memory_test.cc
+++ b/src/reader/spirv/function_memory_test.cc
@@ -31,7 +31,8 @@
   return R"(
     OpCapability Shader
     OpMemoryModel Logical Simple
-    OpEntryPoint Vertex %100 "main"
+    OpEntryPoint Fragment %100 "main"
+    OpExecutionMode %100 OriginUpperLeft
 )";
 }
 
@@ -340,7 +341,7 @@
      OpReturn
   )");
   EXPECT_THAT(err,
-              Eq("15:5: Expected operand, found next instruction instead."));
+              Eq("16:5: Expected operand, found next instruction instead."));
 }
 
 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_BaseIsNotPointer) {
@@ -889,7 +890,7 @@
     Return{}
   }
   Function main -> __void
-  StageDecoration{vertex}
+  StageDecoration{fragment}
   ()
   {
     Call[not set]{
@@ -1291,7 +1292,8 @@
   return R"(
      OpCapability Shader
      OpMemoryModel Logical Simple
-     OpEntryPoint Vertex %100 "main"
+     OpEntryPoint Fragment %100 "main"
+     OpExecutionMode %100 OriginUpperLeft
 
      OpName %myvar "myvar"
      OpMemberName %struct 0 "first"
diff --git a/src/reader/spirv/function_var_test.cc b/src/reader/spirv/function_var_test.cc
index 9b07da7..0030a82 100644
--- a/src/reader/spirv/function_var_test.cc
+++ b/src/reader/spirv/function_var_test.cc
@@ -85,7 +85,8 @@
   return R"(
     OpCapability Shader
     OpMemoryModel Logical Simple
-    OpEntryPoint Vertex %100 "main"
+    OpEntryPoint Fragment %100 "main"
+    OpExecutionMode %100 OriginUpperLeft
 )" + Names(ids);
 }
 
@@ -497,7 +498,8 @@
   auto p = parser(test::Assemble(R"(
      OpCapability Shader
      OpMemoryModel Logical Simple
-     OpEntryPoint Vertex %100 "main"
+     OpEntryPoint Fragment %100 "main"
+     OpExecutionMode %100 OriginUpperLeft
      OpDecorate %arr2uint ArrayStride 16
 )" + CommonTypes() + R"(
      %ptr = OpTypePointer Function %arr2uint
@@ -574,7 +576,8 @@
   auto p = parser(test::Assemble(R"(
      OpCapability Shader
      OpMemoryModel Logical Simple
-     OpEntryPoint Vertex %100 "main"
+     OpEntryPoint Fragment %100 "main"
+     OpExecutionMode %100 OriginUpperLeft
      OpDecorate %arr2uint ArrayStride 16
 )" + CommonTypes() + R"(
      %ptr = OpTypePointer Function %arr2uint
@@ -769,7 +772,8 @@
   auto p = parser(test::Assemble(R"(
       OpCapability Shader
       OpMemoryModel Logical Simple
-      OpEntryPoint Vertex %100 "main"
+      OpEntryPoint Fragment %100 "main"
+      OpExecutionMode %100 OriginUpperLeft
       OpName %_struct_5 "S"
       OpName %_struct_6 "S"
       OpMemberName %_struct_5 0 "algo"
diff --git a/src/reader/spirv/parser_impl_convert_type_test.cc b/src/reader/spirv/parser_impl_convert_type_test.cc
index a6b7d5b..acf2048 100644
--- a/src/reader/spirv/parser_impl_convert_type_test.cc
+++ b/src/reader/spirv/parser_impl_convert_type_test.cc
@@ -27,7 +27,8 @@
   return R"(
     OpCapability Shader
     OpMemoryModel Logical Simple
-    OpEntryPoint Vertex %main "x_100"
+    OpEntryPoint Fragment %main "x_100"
+    OpExecutionMode %main OriginUpperLeft
   )";
 }
 
@@ -75,7 +76,8 @@
      OpCapability Shader
      %1 = OpExtInstImport "GLSL.std.450"
      OpMemoryModel Logical Simple
-     OpEntryPoint Vertex %main "x_100"
+     OpEntryPoint Fragment %main "x_100"
+     OpExecutionMode %main OriginUpperLeft
 )" + MainBody();
   auto p = parser(test::Assemble(assembly));
   EXPECT_TRUE(p->BuildInternalModule());
diff --git a/src/reader/spirv/parser_impl_function_decl_test.cc b/src/reader/spirv/parser_impl_function_decl_test.cc
index 0216660..b4ea30d 100644
--- a/src/reader/spirv/parser_impl_function_decl_test.cc
+++ b/src/reader/spirv/parser_impl_function_decl_test.cc
@@ -32,7 +32,8 @@
 
 std::string Preamble() {
   return Caps() + R"(
-    OpEntryPoint Vertex %main "x_100"
+    OpEntryPoint Fragment %main "x_100"
+    OpExecutionMode %main OriginUpperLeft
   )";
 }
 
@@ -62,10 +63,22 @@
     %float = OpTypeFloat 32
     %uint = OpTypeInt 32 0
     %int = OpTypeInt 32 1
-    %float_0 = OpConstant %float 0.0
   )";
 }
 
+std::string BuiltinPosition() {
+  return R"(OpDecorate %position BuiltIn Position
+    %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %ptr = OpTypePointer Output %v4float
+    %position = OpVariable %ptr Output
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+    %uint = OpTypeInt 32 0
+    %int = OpTypeInt 32 1
+)";
+}
+
 TEST_F(SpvParserTest, EmitFunctions_NoFunctions) {
   auto p = parser(test::Assemble(
       R"(
@@ -95,8 +108,10 @@
 }
 
 TEST_F(SpvParserTest, EmitFunctions_Function_EntryPoint_Vertex) {
-  std::string input = Caps() + R"(OpEntryPoint Vertex %main "main" )" +
-                      Names({"main"}) + CommonTypes() + R"(
+  std::string input = Caps() +
+                      R"(OpEntryPoint Vertex %main "main" %position )" +
+                      Names({"main"}) + BuiltinPosition() + R"(
+
 %main = OpFunction %void None %voidfn
 %entry = OpLabel
 OpReturn
@@ -108,8 +123,14 @@
   Program program = p->program();
   const auto program_ast = program.to_str(false);
   EXPECT_THAT(program_ast, HasSubstr(R"(
+  Struct $3 {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] $4: __vec_4__f32})"))
+      << program_ast;
+
+  EXPECT_THAT(program_ast, HasSubstr(R"(
   Function )" + program.Symbols().Get("main").to_str() +
-                                     R"( -> __void
+                                     R"( -> __type_name_$3
   StageDecoration{vertex}
   ()
   {)"));
@@ -163,8 +184,9 @@
 TEST_F(SpvParserTest, EmitFunctions_Function_EntryPoint_MultipleEntryPoints) {
   std::string input = Caps() +
                       R"(
-OpEntryPoint Vertex %main "first_shader"
-OpEntryPoint Vertex %main "second_shader"
+OpEntryPoint Fragment %main "first_shader"
+OpEntryPoint Fragment %main "second_shader"
+OpExecutionMode %main OriginUpperLeft
 )" + Names({"main"}) + CommonTypes() +
                       MainBody();
 
@@ -176,23 +198,24 @@
   EXPECT_THAT(program_ast, HasSubstr(R"(
   Function )" + program.Symbols().Get("first_shader").to_str() +
                                      R"( -> __void
-  StageDecoration{vertex}
+  StageDecoration{fragment}
   ()
   {)"));
   EXPECT_THAT(program_ast, HasSubstr(R"(
   Function )" + program.Symbols().Get("second_shader").to_str() +
                                      R"( -> __void
-  StageDecoration{vertex}
+  StageDecoration{fragment}
   ()
   {)"));
 }
 
 TEST_F(SpvParserTest,
        EmitFunctions_Function_EntryPoint_GLCompute_LocalSize_Only) {
-  std::string input = Caps() + Names({"main"}) +
-                      R"(OpEntryPoint GLCompute %main "comp_main"
+  std::string input = Caps() + R"(
+OpEntryPoint GLCompute %main "comp_main"
 OpExecutionMode %main LocalSize 2 4 8
-)" + CommonTypes() + R"(
+)" + Names({"main"}) + CommonTypes() +
+                      R"(
 %main = OpFunction %void None %voidfn
 %entry = OpLabel
 OpReturn
@@ -477,6 +500,7 @@
 TEST_F(SpvParserTest, EmitFunctions_NonVoidResultType) {
   auto p = parser(
       test::Assemble(Preamble() + Names({"ret_float"}) + CommonTypes() + R"(
+     %float_0 = OpConstant %float 0.0
      %fn_ret_float = OpTypeFunction %float
 
      %ret_float = OpFunction %float None %fn_ret_float
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index 0ca4db3..11da9b7 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -259,13 +259,19 @@
   // See later for tests where the SPIR-V store type is signed
   // integer, as in GLSL.
   auto p = parser(test::Assemble(Preamble() + R"(
-    OpEntryPoint Vertex %main "main" %52
+    OpEntryPoint Vertex %main "main" %52 %position
+    OpName %position "position"
+    OpDecorate %position BuiltIn Position
     OpDecorate %52 BuiltIn VertexIndex
     %uint = OpTypeInt 32 0
     %ptr = OpTypePointer Input %uint
     %52 = OpVariable %ptr Input
     %void = OpTypeVoid
     %voidfn = OpTypeFunction %void
+    %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %posty = OpTypePointer Output %v4float
+    %position = OpVariable %posty Output
   )" + MainBody()));
 
   EXPECT_TRUE(p->BuildAndParseInternalModule());
@@ -725,17 +731,22 @@
     OpMemoryModel Logical Simple
     OpEntryPoint )" +
          stage + R"( %500 "main" %1
-)" + (stage == "Fragment" ? "OpExecutionMode %500 OriginUpperLeft" : "") +
+)" + (stage == "Vertex" ? " %2 " : "") +
+         +(stage == "Fragment" ? "OpExecutionMode %500 OriginUpperLeft" : "") +
+         +(stage == "Vertex" ? " OpDecorate %2 BuiltIn Position " : "") +
          R"(
     OpDecorate %1 BuiltIn PointSize
     %void = OpTypeVoid
     %voidfn = OpTypeFunction %void
     %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
     %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
     %uint_1 = OpConstant %uint 1
     %11 = OpTypePointer Output %float
     %1 = OpVariable %11 Output
+    %12 = OpTypePointer Output %v4float
+    %2 = OpVariable %12 Output
 )";
 }
 
@@ -755,12 +766,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_EQ(module_str, R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_2_1: __vec_4__f32}
+  }
+  Variable{
+    x_2
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   ()
   {
@@ -769,6 +790,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_2}
+        }
+      }
+    }
   }
 }
 )") << module_str;
@@ -811,6 +840,16 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_EQ(module_str, R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_2_1: __vec_4__f32}
+  }
+  Variable{
+    x_2
+    private
+    undefined
+    __vec_4__f32
+  }
   Variable{
     x_900
     private
@@ -826,7 +865,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   ()
   {
@@ -835,6 +874,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_2}
+        }
+      }
+    }
   }
 }
 )") << module_str;
@@ -880,12 +927,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_EQ(module_str, R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_2_1: __vec_4__f32}
+  }
+  Variable{
+    x_2
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   ()
   {
@@ -894,6 +951,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_2}
+        }
+      }
+    }
   }
 }
 )") << module_str;
@@ -917,12 +982,22 @@
   EXPECT_TRUE(p->error().empty()) << p->error();
   const auto module_str = p->program().to_str();
   EXPECT_EQ(module_str, R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_2_1: __vec_4__f32}
+  }
+  Variable{
+    x_2
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   ()
   {
@@ -931,6 +1006,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_2}
+        }
+      }
+    }
   }
 }
 )") << module_str;
@@ -2479,7 +2562,7 @@
         {
           UnaryOp[not set]{
             indirection
-            Identifier[not set]{x_11}
+            Identifier[not set]{x_14}
           }
         }
       }
@@ -3898,7 +3981,8 @@
   return R"(
     OpCapability Shader
     OpMemoryModel Logical Simple
-    OpEntryPoint Vertex %main "main" %1
+    OpEntryPoint Vertex %main "main" %position %1
+    OpDecorate %position BuiltIn Position
     OpDecorate %1 BuiltIn VertexIndex
     %void = OpTypeVoid
     %voidfn = OpTypeFunction %void
@@ -3908,6 +3992,9 @@
     %ptr_ty = OpTypePointer Input )" +
          store_type + R"(
     %1 = OpVariable %ptr_ty Input
+    %v4float = OpTypeVector %float 4
+    %posty = OpTypePointer Output %v4float
+    %position = OpVariable %posty Output
 )";
 }
 
@@ -3924,12 +4011,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __i32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -3946,7 +4043,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -3971,6 +4068,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";
@@ -3991,18 +4096,28 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __i32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
     VariableDeclStatement{
       VariableConst{
-        x_11
+        x_14
         none
         undefined
         __ptr_none__i32
@@ -4023,14 +4138,14 @@
         {
           UnaryOp[not set]{
             indirection
-            Identifier[not set]{x_11}
+            Identifier[not set]{x_14}
           }
         }
       }
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4055,6 +4170,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";
@@ -4075,12 +4198,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __i32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -4097,7 +4230,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4122,6 +4255,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";
@@ -4141,12 +4282,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __u32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -4163,7 +4314,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4186,6 +4337,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";
@@ -4206,18 +4365,28 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __u32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
     VariableDeclStatement{
       VariableConst{
-        x_11
+        x_14
         none
         undefined
         __ptr_none__u32
@@ -4238,14 +4407,14 @@
         {
           UnaryOp[not set]{
             indirection
-            Identifier[not set]{x_11}
+            Identifier[not set]{x_14}
           }
         }
       }
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4268,6 +4437,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";
@@ -4288,12 +4465,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __u32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -4310,7 +4497,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4333,6 +4520,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";
@@ -4370,7 +4565,9 @@
   return R"(
     OpCapability Shader
     OpMemoryModel Logical Simple
-    OpEntryPoint Vertex %main "main" %1
+    OpEntryPoint Vertex %main "main" %position %1
+    OpName %position "position"
+    OpDecorate %position BuiltIn Position
     OpDecorate %1 BuiltIn InstanceIndex
     %void = OpTypeVoid
     %voidfn = OpTypeFunction %void
@@ -4380,6 +4577,9 @@
     %ptr_ty = OpTypePointer Input )" +
          store_type + R"(
     %1 = OpVariable %ptr_ty Input
+    %v4float = OpTypeVector %float 4
+    %posty = OpTypePointer Output %v4float
+    %position = OpVariable %posty Output
 )";
 }
 
@@ -4396,12 +4596,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] position_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __i32
   }
+  Variable{
+    position
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -4418,7 +4628,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4443,6 +4653,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{position}
+        }
+      }
+    }
   }
 }
 )";
@@ -4463,18 +4681,28 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] position_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __i32
   }
+  Variable{
+    position
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
     VariableDeclStatement{
       VariableConst{
-        x_11
+        x_14
         none
         undefined
         __ptr_none__i32
@@ -4495,14 +4723,14 @@
         {
           UnaryOp[not set]{
             indirection
-            Identifier[not set]{x_11}
+            Identifier[not set]{x_14}
           }
         }
       }
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4527,6 +4755,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{position}
+        }
+      }
+    }
   }
 }
 )";
@@ -4547,12 +4783,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] position_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __i32
   }
+  Variable{
+    position
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -4569,7 +4815,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4594,6 +4840,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{position}
+        }
+      }
+    }
   }
 }
 )";
@@ -4637,12 +4891,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] position_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __u32
   }
+  Variable{
+    position
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -4659,7 +4923,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4682,6 +4946,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{position}
+        }
+      }
+    }
   }
 }
 )";
@@ -4702,18 +4974,28 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] position_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __u32
   }
+  Variable{
+    position
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
     VariableDeclStatement{
       VariableConst{
-        x_11
+        x_14
         none
         undefined
         __ptr_none__u32
@@ -4734,14 +5016,14 @@
         {
           UnaryOp[not set]{
             indirection
-            Identifier[not set]{x_11}
+            Identifier[not set]{x_14}
           }
         }
       }
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4764,6 +5046,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{position}
+        }
+      }
+    }
   }
 }
 )";
@@ -4784,12 +5074,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] position_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __u32
   }
+  Variable{
+    position
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -4806,7 +5106,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -4829,6 +5129,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{position}
+        }
+      }
+    }
   }
 }
 )";
@@ -5523,7 +5831,8 @@
 
 TEST_F(SpvModuleScopeVarParserTest, EntryPointWrapping_IOLocations) {
   const auto assembly = CommonCapabilities() + R"(
-     OpEntryPoint Vertex %main "main" %1 %2 %3 %4
+     OpEntryPoint Fragment %main "main" %1 %2 %3 %4
+     OpExecutionMode %main OriginUpperLeft
      OpDecorate %1 Location 0
      OpDecorate %2 Location 0
      OpDecorate %3 Location 30
@@ -5585,7 +5894,7 @@
     Return{}
   }
   Function main -> __type_name_main_out
-  StageDecoration{vertex}
+  StageDecoration{fragment}
   (
     VariableConst{
       Decorations{
@@ -5640,12 +5949,15 @@
   // instance_index is u32 in WGSL. Use uint in SPIR-V.
   // No bitcasts are used for parameter formation or return value.
   const auto assembly = CommonCapabilities() + R"(
-     OpEntryPoint Vertex %main "main" %1
+     OpEntryPoint Vertex %main "main" %1 %position
+     OpDecorate %position BuiltIn Position
      OpDecorate %1 BuiltIn InstanceIndex
 )" + CommonTypes() +
                         R"(
      %ptr_in_uint = OpTypePointer Input %uint
      %1 = OpVariable %ptr_in_uint Input
+     %posty = OpTypePointer Output %v4float
+     %position = OpVariable %posty Output
 
      %main = OpFunction %void None %voidfn
      %entry = OpLabel
@@ -5660,12 +5972,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto got = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __u32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -5682,7 +6004,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -5705,6 +6027,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";
@@ -5715,12 +6045,15 @@
        EntryPointWrapping_BuiltinVar_Input_OppositeSignedness) {
   // instance_index is u32 in WGSL. Use int in SPIR-V.
   const auto assembly = CommonCapabilities() + R"(
-     OpEntryPoint Vertex %main "main" %1
+     OpEntryPoint Vertex %main "main" %position %1
+     OpDecorate %position BuiltIn Position
      OpDecorate %1 BuiltIn InstanceIndex
 )" + CommonTypes() +
                         R"(
      %ptr_in_int = OpTypePointer Input %int
      %1 = OpVariable %ptr_in_int Input
+     %posty = OpTypePointer Output %v4float
+     %position = OpVariable %posty Output
 
      %main = OpFunction %void None %voidfn
      %entry = OpLabel
@@ -5735,12 +6068,22 @@
   EXPECT_TRUE(p->error().empty());
   const auto got = p->program().to_str();
   const std::string expected = R"(Module{
+  Struct main_out {
+    StructMember{[[ BuiltinDecoration{position}
+ ]] x_4_1: __vec_4__f32}
+  }
   Variable{
     x_1
     private
     undefined
     __i32
   }
+  Variable{
+    x_4
+    private
+    undefined
+    __vec_4__f32
+  }
   Function main_1 -> __void
   ()
   {
@@ -5757,7 +6100,7 @@
     }
     Return{}
   }
-  Function main -> __void
+  Function main -> __type_name_main_out
   StageDecoration{vertex}
   (
     VariableConst{
@@ -5782,6 +6125,14 @@
       (
       )
     }
+    Return{
+      {
+        TypeConstructor[not set]{
+          __type_name_main_out
+          Identifier[not set]{x_4}
+        }
+      }
+    }
   }
 }
 )";