spirv-reader: update tests to use valid SPIR-V

This CL: all SPIR-V modules in parser_impl_module_var_test.cc

Bug: tint:765
Change-Id: I2e8427aea729793a4e34e2ac0c6fc689f65bda04
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49645
Auto-Submit: David Neto <dneto@google.com>
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: Alan Baker <alanbaker@google.com>
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index adc8cc5..b28b0a7 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -29,6 +29,29 @@
 using ::testing::HasSubstr;
 using ::testing::Not;
 
+std::string Preamble() {
+  return R"(
+   OpCapability Shader
+   OpMemoryModel Logical Simple
+)";
+}
+
+std::string FragMain() {
+  return R"(
+   OpEntryPoint Fragment %main "main"
+   OpExecutionMode %main OriginUpperLeft
+)";
+}
+
+std::string MainBody() {
+  return R"(
+   %main = OpFunction %void None %voidfn
+   %main_entry = OpLabel
+   OpReturn
+   OpFunctionEnd
+)";
+}
+
 std::string CommonTypes() {
   return R"(
     %void = OpTypeVoid
@@ -63,20 +86,33 @@
   )";
 }
 
+// Returns layout annotations for types in CommonTypes()
+std::string CommonLayout() {
+  return R"(
+    OpMemberDecorate %strct 0 Offset 0
+    OpMemberDecorate %strct 1 Offset 4
+    OpMemberDecorate %strct 2 Offset 8
+    OpDecorate %arr2uint ArrayStride 4
+)";
+}
+
 TEST_F(SpvModuleScopeVarParserTest, NoVar) {
-  auto p = parser(test::Assemble(""));
-  EXPECT_TRUE(p->BuildAndParseInternalModule());
+  auto assembly = Preamble() + FragMain() + CommonTypes() + MainBody();
+  auto p = parser(test::Assemble(assembly));
+  EXPECT_TRUE(p->BuildAndParseInternalModule()) << assembly;
   EXPECT_TRUE(p->error().empty());
   const auto module_ast = p->program().to_str();
-  EXPECT_THAT(module_ast, Not(HasSubstr("Variable")));
+  EXPECT_THAT(module_ast, Not(HasSubstr("Variable"))) << module_ast;
 }
 
 TEST_F(SpvModuleScopeVarParserTest, BadStorageClass_NotAWebGPUStorageClass) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     %float = OpTypeFloat 32
     %ptr = OpTypePointer CrossWorkgroup %float
     %52 = OpVariable %ptr CrossWorkgroup
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   EXPECT_TRUE(p->BuildInternalModule());
   // Normally we should run ParserImpl::RegisterTypes before emitting
   // variables. But defensive coding in EmitModuleScopeVariables lets
@@ -86,11 +122,13 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, BadStorageClass_Function) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     %float = OpTypeFloat 32
     %ptr = OpTypePointer Function %float
     %52 = OpVariable %ptr Function
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   EXPECT_TRUE(p->BuildInternalModule());
   // Normally we should run ParserImpl::RegisterTypes before emitting
   // variables. But defensive coding in EmitModuleScopeVariables lets
@@ -98,16 +136,18 @@
   EXPECT_FALSE(p->EmitModuleScopeVariables()) << p->error();
   EXPECT_THAT(p->error(),
               HasSubstr("invalid SPIR-V storage class 7 for module scope "
-                        "variable: %52 = OpVariable %2 Function"));
+                        "variable: %52 = OpVariable %3 Function"));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, BadPointerType) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     %float = OpTypeFloat 32
     %fn_ty = OpTypeFunction %float
     %3 = OpTypePointer Private %fn_ty
     %52 = OpVariable %3 Private
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   EXPECT_TRUE(p->BuildInternalModule());
   // Normally we should run ParserImpl::RegisterTypes before emitting
   // variables. But defensive coding in EmitModuleScopeVariables lets
@@ -118,12 +158,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, NonPointerType) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     %float = OpTypeFloat 32
     %5 = OpTypeFunction %float
     %3 = OpTypePointer Private %5
     %52 = OpVariable %float Private
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   EXPECT_TRUE(p->BuildInternalModule());
   EXPECT_FALSE(p->RegisterTypes());
   EXPECT_THAT(
@@ -132,11 +174,13 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, AnonWorkgroupVar) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     %float = OpTypeFloat 32
     %ptr = OpTypePointer Workgroup %float
     %52 = OpVariable %ptr Workgroup
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
 
   EXPECT_TRUE(p->BuildAndParseInternalModule());
   EXPECT_TRUE(p->error().empty());
@@ -150,12 +194,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, NamedWorkgroupVar) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     OpName %52 "the_counter"
     %float = OpTypeFloat 32
     %ptr = OpTypePointer Workgroup %float
     %52 = OpVariable %ptr Workgroup
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
 
   EXPECT_TRUE(p->BuildAndParseInternalModule());
   EXPECT_TRUE(p->error().empty());
@@ -169,12 +215,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, PrivateVar) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     OpName %52 "my_own_private_idaho"
     %float = OpTypeFloat 32
     %ptr = OpTypePointer Private %float
     %52 = OpVariable %ptr Private
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
 
   EXPECT_TRUE(p->BuildAndParseInternalModule());
   EXPECT_TRUE(p->error().empty());
@@ -192,12 +240,15 @@
   // where the SPIR-V uses the same store type as in WGSL.
   // See later for tests where the SPIR-V store type is signed
   // integer, as in GLSL.
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + R"(
+    OpEntryPoint Vertex %main "main" %52
     OpDecorate %52 BuiltIn VertexIndex
     %uint = OpTypeInt 32 0
     %ptr = OpTypePointer Input %uint
     %52 = OpVariable %ptr Input
-  )"));
+    %void = OpTypeVoid
+    %voidfn = OpTypeFunction %void
+  )" + MainBody()));
 
   EXPECT_TRUE(p->BuildAndParseInternalModule());
   EXPECT_TRUE(p->error().empty());
@@ -216,8 +267,8 @@
 std::string PerVertexPreamble() {
   return R"(
     OpCapability Shader
-    OpCapability Linkage ; so we don't have to declare an entry point
     OpMemoryModel Logical Simple
+    OpEntryPoint Vertex %main "main" %1
 
     OpMemberDecorate %10 0 BuiltIn Position
     OpMemberDecorate %10 1 BuiltIn PointSize
@@ -239,7 +290,12 @@
 
 TEST_F(SpvModuleScopeVarParserTest, BuiltinPosition_MapsToModuleScopeVec4Var) {
   // In Vulkan SPIR-V, Position is the first member of gl_PerVertex
-  const std::string assembly = PerVertexPreamble();
+  const std::string assembly = PerVertexPreamble() + R"(
+  %main = OpFunction %void None %voidfn
+  %entry = OpLabel
+  OpReturn
+  OpFunctionEnd
+)";
   auto p = parser(test::Assemble(assembly));
 
   EXPECT_TRUE(p->BuildAndParseInternalModule()) << assembly;
@@ -279,7 +335,7 @@
   auto p = parser(test::Assemble(assembly));
   EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
   EXPECT_THAT(p->error(), Eq("storing to the whole per-vertex structure is not "
-                             "supported: OpStore %1 %9"))
+                             "supported: OpStore %1 %13"))
       << p->error();
 }
 
@@ -350,8 +406,8 @@
        BuiltinPosition_StorePosition_PerVertexStructOutOfOrderDecl) {
   const std::string assembly = R"(
   OpCapability Shader
-  OpCapability Linkage ; so we don't have to declare an entry point
   OpMemoryModel Logical Simple
+  OpEntryPoint Vertex %main "main" %1
 
  ;  scramble the member indices
   OpMemberDecorate %10 0 BuiltIn ClipDistance
@@ -486,7 +542,8 @@
     out
     __vec_4__f32
   }
-  Function x_14 -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Return{}
@@ -547,7 +604,8 @@
     out
     __vec_4__f32
   }
-  Function x_15 -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Assignment{
@@ -585,7 +643,7 @@
 TEST_F(SpvModuleScopeVarParserTest,
        BuiltinPointSize_WriteViaCopyObjectPostAccessChainErased) {
   const std::string assembly = PerVertexPreamble() + R"(
-  %ptr = OpTypePointer Output %12
+  %ptr = OpTypePointer Output %float
   %one = OpConstant %float 1.0
 
   %main = OpFunction %void None %voidfn
@@ -609,7 +667,8 @@
     out
     __vec_4__f32
   }
-  Function x_14 -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Return{}
@@ -618,12 +677,14 @@
 )") << module_str;
 }
 
-std::string LoosePointSizePreamble() {
+std::string LoosePointSizePreamble(std::string stage = "Vertex") {
   return R"(
     OpCapability Shader
-    OpCapability Linkage ; so we don't have to declare an entry point
     OpMemoryModel Logical Simple
-
+    OpEntryPoint )" +
+         stage + R"( %500 "main" %1
+)" + (stage == "Fragment" ? "OpExecutionMode %500 OriginUpperLeft" : "") +
+         R"(
     OpDecorate %1 BuiltIn PointSize
     %void = OpTypeVoid
     %voidfn = OpTypeFunction %void
@@ -652,7 +713,8 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_EQ(module_str, R"(Module{
-  Function x_500 -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Return{}
@@ -679,7 +741,8 @@
                         "PointSize builtin: OpStore %1 %999"));
 }
 
-TEST_F(SpvModuleScopeVarParserTest, BuiltinPointSize_Loose_ReadReplaced) {
+TEST_F(SpvModuleScopeVarParserTest,
+       BuiltinPointSize_Loose_ReadReplaced_Vertex) {
   const std::string assembly = LoosePointSizePreamble() + R"(
   %ptr = OpTypePointer Private %float
   %900 = OpVariable %ptr Private
@@ -692,6 +755,7 @@
   OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
+
   EXPECT_TRUE(p->BuildAndParseInternalModule());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -701,7 +765,8 @@
     private
     __f32
   }
-  Function x_500 -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Assignment{
@@ -715,6 +780,28 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest,
+       BuiltinPointSize_Loose_ReadReplaced_Fragment) {
+  const std::string assembly = LoosePointSizePreamble("Fragment") + R"(
+  %ptr = OpTypePointer Private %float
+  %900 = OpVariable %ptr Private
+
+  %500 = OpFunction %void None %voidfn
+  %entry = OpLabel
+  %99 = OpLoad %float %1
+  OpStore %900 %99
+  OpReturn
+  OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+
+  // This example is invalid because you PointSize is not valid in Vulkan
+  // Fragment shaders.
+  EXPECT_FALSE(p->Parse());
+  EXPECT_FALSE(p->success());
+  EXPECT_THAT(p->error(), HasSubstr("VUID-PointSize-PointSize-04314"));
+}
+
+TEST_F(SpvModuleScopeVarParserTest,
        BuiltinPointSize_Loose_WriteViaCopyObjectPriorAccess_Erased) {
   const std::string assembly = LoosePointSizePreamble() + R"(
   %one = OpConstant %float 1.0
@@ -732,7 +819,8 @@
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_EQ(module_str, R"(Module{
-  Function x_500 -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Return{}
@@ -759,7 +847,8 @@
   EXPECT_TRUE(p->error().empty()) << p->error();
   const auto module_str = p->program().to_str();
   EXPECT_EQ(module_str, R"(Module{
-  Function x_500 -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Return{}
@@ -827,7 +916,7 @@
   EXPECT_FALSE(p->BuildAndParseInternalModule());
   EXPECT_THAT(p->error(),
               Eq("first index of access chain into per-vertex structure is not "
-                 "a constant: %100 = OpAccessChain %9 %1 %16"));
+                 "a constant: %100 = OpAccessChain %13 %1 %16"));
 }
 
 TEST_F(SpvModuleScopeVarParserTest,
@@ -848,17 +937,17 @@
   EXPECT_FALSE(p->BuildAndParseInternalModule());
   EXPECT_THAT(p->error(),
               Eq("first index of access chain into per-vertex structure is not "
-                 "a constant integer: %100 = OpAccessChain %9 %1 %13"));
+                 "a constant integer: %100 = OpAccessChain %13 %1 %14"));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarInitializers) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %1 = OpVariable %ptr_bool Private %true
      %2 = OpVariable %ptr_bool Private %false
      %3 = OpVariable %ptr_int Private %int_m1
      %4 = OpVariable %ptr_uint Private %uint_1
      %5 = OpVariable %ptr_float Private %float_1p5
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -905,7 +994,7 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarNullInitializers) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %null_bool = OpConstantNull %bool
      %null_int = OpConstantNull %int
      %null_uint = OpConstantNull %uint
@@ -915,7 +1004,7 @@
      %2 = OpVariable %ptr_int Private %null_int
      %3 = OpVariable %ptr_uint Private %null_uint
      %4 = OpVariable %ptr_float Private %null_float
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -954,7 +1043,7 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarUndefInitializers) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %undef_bool = OpUndef %bool
      %undef_int = OpUndef %int
      %undef_uint = OpUndef %uint
@@ -964,7 +1053,7 @@
      %2 = OpVariable %ptr_int Private %undef_int
      %3 = OpVariable %ptr_uint Private %undef_uint
      %4 = OpVariable %ptr_float Private %undef_float
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1000,15 +1089,18 @@
       ScalarConstructor[not set]{0.000000}
     }
   })"));
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2float
      %two = OpConstant %float 2.0
      %const = OpConstantComposite %v2float %float_1p5 %two
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1027,11 +1119,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorBoolNullInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2bool
      %const = OpConstantNull %v2bool
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1050,11 +1142,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorBoolUndefInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2bool
      %const = OpUndef %v2bool
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1070,14 +1162,17 @@
       }
     }
   })"));
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorUintNullInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2uint
      %const = OpConstantNull %v2uint
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1096,11 +1191,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorUintUndefInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2uint
      %const = OpUndef %v2uint
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1116,14 +1211,17 @@
       }
     }
   })"));
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorIntNullInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2int
      %const = OpConstantNull %v2int
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1142,11 +1240,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorIntUndefInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2int
      %const = OpUndef %v2int
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1162,14 +1260,17 @@
       }
     }
   })"));
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorFloatNullInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2float
      %const = OpConstantNull %v2float
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1188,11 +1289,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, VectorFloatUndefInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %v2float
      %const = OpUndef %v2float
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1208,10 +1309,13 @@
       }
     }
   })"));
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, MatrixInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %m3v2float
      %two = OpConstant %float 2.0
      %three = OpConstant %float 3.0
@@ -1221,7 +1325,7 @@
      %v2 = OpConstantComposite %v2float %three %four
      %const = OpConstantComposite %m3v2float %v0 %v1 %v2
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1253,11 +1357,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, MatrixNullInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %m3v2float
      %const = OpConstantNull %m3v2float
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1289,11 +1393,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, MatrixUndefInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %m3v2float
      %const = OpUndef %m3v2float
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1322,15 +1426,18 @@
       }
     }
   })"));
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ArrayInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %arr2uint
      %two = OpConstant %uint 2
      %const = OpConstantComposite %arr2uint %uint_1 %two
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1349,11 +1456,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ArrayNullInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %arr2uint
      %const = OpConstantNull %arr2uint
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1372,11 +1479,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ArrayUndefInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %arr2uint
      %const = OpUndef %arr2uint
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1392,16 +1499,19 @@
       }
     }
   })"));
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, StructInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %strct
      %two = OpConstant %uint 2
      %arrconst = OpConstantComposite %arr2uint %uint_1 %two
      %const = OpConstantComposite %strct %uint_1 %float_1p5 %arrconst
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1426,11 +1536,11 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, StructNullInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %strct
      %const = OpConstantNull %strct
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1455,13 +1565,15 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, StructUndefInitializer) {
-  auto p = parser(test::Assemble(CommonTypes() + R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
      %ptr = OpTypePointer Private %strct
      %const = OpUndef %strct
      %200 = OpVariable %ptr Private %const
-  )"));
+  )" + MainBody()));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
+
   const auto module_str = p->program().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
@@ -1481,16 +1593,19 @@
     }
   })"))
       << module_str;
+
+  // This example module emits ok, but is not valid SPIR-V in the first place.
+  p->DeliberatelyInvalidSpirv();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, LocationDecoration_Valid) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %myvar Location 3
 )" + CommonTypes() + R"(
      %ptr = OpTypePointer Input %uint
      %myvar = OpVariable %ptr Input
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1508,41 +1623,40 @@
 
 TEST_F(SpvModuleScopeVarParserTest,
        LocationDecoration_MissingOperandWontAssemble) {
-  const auto assembly = R"(
+  const auto assembly = Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %myvar Location
 )" + CommonTypes() + R"(
      %ptr = OpTypePointer Input %uint
      %myvar = OpVariable %ptr Input
-  )";
+  )" + MainBody();
   EXPECT_THAT(test::AssembleFailure(assembly),
-              Eq("4:4: Expected operand, found next instruction instead."));
+              Eq("10:4: Expected operand, found next instruction instead."));
 }
 
 TEST_F(SpvModuleScopeVarParserTest,
        LocationDecoration_TwoOperandsWontAssemble) {
-  const auto assembly = R"(
+  const auto assembly = Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %myvar Location 3 4
 )" + CommonTypes() + R"(
      %ptr = OpTypePointer Input %uint
      %myvar = OpVariable %ptr Input
-  )";
+  )" + MainBody();
   EXPECT_THAT(
       test::AssembleFailure(assembly),
-      Eq("2:34: Expected <opcode> or <result-id> at the beginning of an "
+      Eq("8:34: Expected <opcode> or <result-id> at the beginning of an "
          "instruction, found '4'."));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, DescriptorGroupDecoration_Valid) {
-  auto p = parser(test::Assemble(R"(
-     OpName %myvar "myvar"
-     OpDecorate %myvar DescriptorSet 3
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonLayout() + R"(
+     OpDecorate %1 DescriptorSet 3
      OpDecorate %strct Block
 )" + CommonTypes() + R"(
      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
-     %myvar = OpVariable %ptr_sb_strct StorageBuffer
-  )"));
+     %1 = OpVariable %ptr_sb_strct StorageBuffer
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1551,7 +1665,7 @@
     Decorations{
       GroupDecoration{3}
     }
-    myvar
+    x_1
     storage
     __access_control_read_write__type_name_S
   })"))
@@ -1560,43 +1674,42 @@
 
 TEST_F(SpvModuleScopeVarParserTest,
        DescriptorGroupDecoration_MissingOperandWontAssemble) {
-  const auto assembly = R"(
-     OpName %myvar "myvar"
-     OpDecorate %myvar DescriptorSet
+  const auto assembly = Preamble() + FragMain() + CommonLayout() + R"(
+     OpDecorate %1 DescriptorSet
      OpDecorate %strct Block
 )" + CommonTypes() + R"(
      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
-     %myvar = OpVariable %ptr_sb_strct StorageBuffer
-  )";
+     %1 = OpVariable %ptr_sb_strct StorageBuffer
+  )" + MainBody();
   EXPECT_THAT(test::AssembleFailure(assembly),
-              Eq("3:5: Expected operand, found next instruction instead."));
+              Eq("13:5: Expected operand, found next instruction instead."));
 }
 
 TEST_F(SpvModuleScopeVarParserTest,
        DescriptorGroupDecoration_TwoOperandsWontAssemble) {
-  const auto assembly = R"(
+  const auto assembly = Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %myvar DescriptorSet 3 4
      OpDecorate %strct Block
 )" + CommonTypes() + R"(
      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
      %myvar = OpVariable %ptr_sb_strct StorageBuffer
-  )";
+  )" + MainBody();
   EXPECT_THAT(
       test::AssembleFailure(assembly),
-      Eq("2:39: Expected <opcode> or <result-id> at the beginning of an "
+      Eq("8:39: Expected <opcode> or <result-id> at the beginning of an "
          "instruction, found '4'."));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, BindingDecoration_Valid) {
-  auto p = parser(test::Assemble(R"(
-     OpName %myvar "myvar"
-     OpDecorate %myvar Binding 3
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
+     OpDecorate %1 Binding 3
      OpDecorate %strct Block
-)" + CommonTypes() + R"(
+)" + CommonLayout() + CommonTypes() +
+                                 R"(
      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
-     %myvar = OpVariable %ptr_sb_strct StorageBuffer
-  )"));
+     %1 = OpVariable %ptr_sb_strct StorageBuffer
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1605,7 +1718,7 @@
     Decorations{
       BindingDecoration{3}
     }
-    myvar
+    x_1
     storage
     __access_control_read_write__type_name_S
   })"))
@@ -1614,55 +1727,55 @@
 
 TEST_F(SpvModuleScopeVarParserTest,
        BindingDecoration_MissingOperandWontAssemble) {
-  const auto assembly = R"(
+  const auto assembly = Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %myvar Binding
      OpDecorate %strct Block
 )" + CommonTypes() + R"(
      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
      %myvar = OpVariable %ptr_sb_strct StorageBuffer
-  )";
+  )" + MainBody();
   EXPECT_THAT(test::AssembleFailure(assembly),
-              Eq("3:5: Expected operand, found next instruction instead."));
+              Eq("9:5: Expected operand, found next instruction instead."));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, BindingDecoration_TwoOperandsWontAssemble) {
-  const auto assembly = R"(
+  const auto assembly = Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %myvar Binding 3 4
      OpDecorate %strct Block
 )" + CommonTypes() + R"(
      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
      %myvar = OpVariable %ptr_sb_strct StorageBuffer
-  )";
+  )" + MainBody();
   EXPECT_THAT(
       test::AssembleFailure(assembly),
-      Eq("2:33: Expected <opcode> or <result-id> at the beginning of an "
+      Eq("8:33: Expected <opcode> or <result-id> at the beginning of an "
          "instruction, found '4'."));
 }
 
 TEST_F(SpvModuleScopeVarParserTest,
        StructMember_NonReadableDecoration_Dropped) {
-  auto p = parser(test::Assemble(R"(
-     OpName %myvar "myvar"
+  auto p = parser(test::Assemble(Preamble() + FragMain() + CommonLayout() + R"(
      OpDecorate %strct Block
      OpMemberDecorate %strct 0 NonReadable
 )" + CommonTypes() + R"(
      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
-     %myvar = OpVariable %ptr_sb_strct StorageBuffer
-  )"));
+     %1 = OpVariable %ptr_sb_strct StorageBuffer
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(
+  Arr -> __array__u32_2_stride_4
   Struct S {
     [[block]]
-    StructMember{field0: __u32}
-    StructMember{field1: __f32}
-    StructMember{field2: __array__u32_2}
+    StructMember{[[ offset 0 ]] field0: __u32}
+    StructMember{[[ offset 4 ]] field1: __f32}
+    StructMember{[[ offset 8 ]] field2: __alias_Arr__array__u32_2_stride_4}
   }
   Variable{
-    myvar
+    x_1
     storage
     __access_control_read_write__type_name_S
   }
@@ -1670,10 +1783,12 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ColMajorDecoration_Dropped) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %s Block
      OpMemberDecorate %s 0 ColMajor
+     OpMemberDecorate %s 0 Offset 0
+     OpMemberDecorate %s 0 MatrixStride 8
      %float = OpTypeFloat 32
      %v2float = OpTypeVector %float 2
      %m3v2float = OpTypeMatrix %v2float 3
@@ -1681,14 +1796,16 @@
      %s = OpTypeStruct %m3v2float
      %ptr_sb_s = OpTypePointer StorageBuffer %s
      %myvar = OpVariable %ptr_sb_s StorageBuffer
-  )"));
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(
   Struct S {
     [[block]]
-    StructMember{field0: __mat_2_3__f32}
+    StructMember{[[ offset 0 ]] field0: __mat_2_3__f32}
   }
   Variable{
     myvar
@@ -1699,10 +1816,13 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, MatrixStrideDecoration_Dropped) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %s Block
      OpMemberDecorate %s 0 MatrixStride 8
+     OpMemberDecorate %s 0 Offset 0
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
      %v2float = OpTypeVector %float 2
      %m3v2float = OpTypeMatrix %v2float 3
@@ -1710,14 +1830,14 @@
      %s = OpTypeStruct %m3v2float
      %ptr_sb_s = OpTypePointer StorageBuffer %s
      %myvar = OpVariable %ptr_sb_s StorageBuffer
-  )"));
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(
   Struct S {
     [[block]]
-    StructMember{field0: __mat_2_3__f32}
+    StructMember{[[ offset 0 ]] field0: __mat_2_3__f32}
   }
   Variable{
     myvar
@@ -1728,10 +1848,13 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, RowMajorDecoration_IsError) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %myvar "myvar"
      OpDecorate %s Block
      OpMemberDecorate %s 0 RowMajor
+     OpMemberDecorate %s 0 Offset 0
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
      %v2float = OpTypeVector %float 2
      %m3v2float = OpTypeMatrix %v2float 3
@@ -1739,38 +1862,41 @@
      %s = OpTypeStruct %m3v2float
      %ptr_sb_s = OpTypePointer StorageBuffer %s
      %myvar = OpVariable %ptr_sb_s StorageBuffer
-  )"));
+  )" + MainBody()));
   EXPECT_FALSE(p->BuildAndParseInternalModuleExceptFunctions());
   EXPECT_THAT(
       p->error(),
-      Eq(R"(WGSL does not support row-major matrices: can't translate member 0 of %2 = OpTypeStruct %5)"))
+      Eq(R"(WGSL does not support row-major matrices: can't translate member 0 of %3 = OpTypeStruct %8)"))
       << p->error();
 }
 
 TEST_F(SpvModuleScopeVarParserTest, StorageBuffer_NonWritable_AllMembers) {
   // Variable should have access(read)
-  auto p = parser(test::Assemble(R"(
-     OpName %myvar "myvar"
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpDecorate %s Block
      OpMemberDecorate %s 0 NonWritable
      OpMemberDecorate %s 1 NonWritable
+     OpMemberDecorate %s 0 Offset 0
+     OpMemberDecorate %s 1 Offset 4
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
 
      %s = OpTypeStruct %float %float
      %ptr_sb_s = OpTypePointer StorageBuffer %s
-     %myvar = OpVariable %ptr_sb_s StorageBuffer
-  )"));
+     %1 = OpVariable %ptr_sb_s StorageBuffer
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(
   Struct S {
     [[block]]
-    StructMember{field0: __f32}
-    StructMember{field1: __f32}
+    StructMember{[[ offset 0 ]] field0: __f32}
+    StructMember{[[ offset 4 ]] field1: __f32}
   }
   Variable{
-    myvar
+    x_1
     storage
     __access_control_read_only__type_name_S
   }
@@ -1779,27 +1905,30 @@
 
 TEST_F(SpvModuleScopeVarParserTest, StorageBuffer_NonWritable_NotAllMembers) {
   // Variable should have access(read_write)
-  auto p = parser(test::Assemble(R"(
-     OpName %myvar "myvar"
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpDecorate %s Block
      OpMemberDecorate %s 0 NonWritable
+     OpMemberDecorate %s 0 Offset 0
+     OpMemberDecorate %s 1 Offset 4
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
 
      %s = OpTypeStruct %float %float
      %ptr_sb_s = OpTypePointer StorageBuffer %s
-     %myvar = OpVariable %ptr_sb_s StorageBuffer
-  )"));
+     %1 = OpVariable %ptr_sb_s StorageBuffer
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(
   Struct S {
     [[block]]
-    StructMember{field0: __f32}
-    StructMember{field1: __f32}
+    StructMember{[[ offset 0 ]] field0: __f32}
+    StructMember{[[ offset 4 ]] field1: __f32}
   }
   Variable{
-    myvar
+    x_1
     storage
     __access_control_read_write__type_name_S
   }
@@ -1810,28 +1939,31 @@
     SpvModuleScopeVarParserTest,
     StorageBuffer_NonWritable_NotAllMembers_DuplicatedOnSameMember) {  // NOLINT
   // Variable should have access(read_write)
-  auto p = parser(test::Assemble(R"(
-     OpName %myvar "myvar"
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpDecorate %s Block
      OpMemberDecorate %s 0 NonWritable
      OpMemberDecorate %s 0 NonWritable ; same member. Don't double-count it
+     OpMemberDecorate %s 0 Offset 0
+     OpMemberDecorate %s 1 Offset 4
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
 
      %s = OpTypeStruct %float %float
      %ptr_sb_s = OpTypePointer StorageBuffer %s
-     %myvar = OpVariable %ptr_sb_s StorageBuffer
-  )"));
+     %1 = OpVariable %ptr_sb_s StorageBuffer
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(
   Struct S {
     [[block]]
-    StructMember{field0: __f32}
-    StructMember{field1: __f32}
+    StructMember{[[ offset 0 ]] field0: __f32}
+    StructMember{[[ offset 4 ]] field1: __f32}
   }
   Variable{
-    myvar
+    x_1
     storage
     __access_control_read_write__type_name_S
   }
@@ -1839,12 +1971,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_True) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %c "myconst"
      OpDecorate %c SpecId 12
      %bool = OpTypeBool
      %c = OpSpecConstantTrue %bool
-  )"));
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1864,12 +1998,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_False) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %c "myconst"
      OpDecorate %c SpecId 12
      %bool = OpTypeBool
      %c = OpSpecConstantFalse %bool
-  )"));
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1889,12 +2025,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_U32) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %c "myconst"
      OpDecorate %c SpecId 12
      %uint = OpTypeInt 32 0
      %c = OpSpecConstant %uint 42
-  )"));
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1914,12 +2052,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_I32) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %c "myconst"
      OpDecorate %c SpecId 12
      %int = OpTypeInt 32 1
      %c = OpSpecConstant %int 42
-  )"));
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1939,12 +2079,14 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_F32) {
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %c "myconst"
      OpDecorate %c SpecId 12
      %float = OpTypeFloat 32
      %c = OpSpecConstant %float 2.5
-  )"));
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1966,11 +2108,13 @@
 TEST_F(SpvModuleScopeVarParserTest,
        ScalarSpecConstant_DeclareConst_F32_WithoutSpecId) {
   // When we don't have a spec ID, declare an undecorated module-scope constant.
-  auto p = parser(test::Assemble(R"(
+  auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
      OpName %c "myconst"
      %float = OpTypeFloat 32
      %c = OpSpecConstant %float 2.5
-  )"));
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+  )" + MainBody()));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->program().to_str();
@@ -1987,38 +2131,38 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_UsedInFunction) {
-  auto p = parser(test::Assemble(R"(
+  const auto assembly = Preamble() + FragMain() + R"(
      OpName %c "myconst"
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
      %c = OpSpecConstant %float 2.5
      %floatfn = OpTypeFunction %float
      %100 = OpFunction %float None %floatfn
      %entry = OpLabel
-     %1 = OpIAdd %float %c %c
-     OpReturn
+     %1 = OpFAdd %float %c %c
+     OpReturnValue %1
      OpFunctionEnd
-  )"));
+  )" + MainBody();
+  std::cout << assembly << std::endl;
+  auto p = parser(test::Assemble(assembly));
   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_TRUE(p->error().empty());
 
   Program program = p->program();
+  const auto got = ToString(program, fe.ast_body());
 
-  EXPECT_THAT(ToString(program, fe.ast_body()), HasSubstr(R"(
-  VariableConst{
-    x_1
-    none
-    __f32
-    {
-      Binary[not set]{
-        Identifier[not set]{myconst}
-        add
-        Identifier[not set]{myconst}
-      }
+  EXPECT_THAT(got, HasSubstr(R"(Return{
+  {
+    Binary[not set]{
+      Identifier[not set]{myconst}
+      add
+      Identifier[not set]{myconst}
     }
-  })"))
-      << ToString(program, fe.ast_body());
+  }
+})")) << got;
 }
 
 // Returns the start of a shader for testing SampleId,
@@ -2336,71 +2480,11 @@
     OpFunctionEnd
  )";
   auto p = parser(test::Assemble(assembly));
-  // TODO(dneto): We can handle this if we make a shadow variable and mutate
-  // the parameter type.
-  ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
-  EXPECT_TRUE(p->error().empty());
-  const auto module_str = p->program().to_str();
-  // Correct declaration
-  EXPECT_THAT(module_str, HasSubstr(R"(
-  Variable{
-    Decorations{
-      BuiltinDecoration{sample_index}
-    }
-    x_1
-    in
-    __u32
-  })"));
-
-  // Correct bodies
-  EXPECT_THAT(module_str, HasSubstr(R"(
-  Function x_11 -> __u32
-  (
-    VariableConst{
-      x_12
-      none
-      __ptr_in__u32
-    }
-  )
-  {
-    VariableDeclStatement{
-      VariableConst{
-        x_3
-        none
-        __u32
-        {
-          Identifier[not set]{x_12}
-        }
-      }
-    }
-    Return{
-      {
-        Identifier[not set]{x_3}
-      }
-    }
-  }
-  Function main -> __void
-  StageDecoration{fragment}
-  ()
-  {
-    VariableDeclStatement{
-      VariableConst{
-        x_15
-        none
-        __u32
-        {
-          Call[not set]{
-            Identifier[not set]{x_11}
-            (
-              Identifier[not set]{x_1}
-            )
-          }
-        }
-      }
-    }
-    Return{}
-  }
-})")) << module_str;
+  // This example is invalid because you can't pass pointer-to-Input
+  // as a function parameter.
+  EXPECT_FALSE(p->Parse());
+  EXPECT_THAT(p->error(),
+              HasSubstr("Invalid storage class for pointer operand 1"));
 }
 
 // Returns the start of a shader for testing SampleMask
@@ -3278,71 +3362,12 @@
     OpFunctionEnd
  )";
   auto p = parser(test::Assemble(assembly));
-  // TODO(dneto): We can handle this if we make a shadow variable and mutate
-  // the parameter type.
-  ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
-  EXPECT_TRUE(p->error().empty());
-  const auto module_str = p->program().to_str();
-  // Correct declaration
-  EXPECT_THAT(module_str, HasSubstr(R"(
-  Variable{
-    Decorations{
-      BuiltinDecoration{vertex_index}
-    }
-    x_1
-    in
-    __u32
-  })"));
 
-  // Correct bodies
-  EXPECT_THAT(module_str, HasSubstr(R"(
-  Function x_11 -> __u32
-  (
-    VariableConst{
-      x_12
-      none
-      __ptr_in__u32
-    }
-  )
-  {
-    VariableDeclStatement{
-      VariableConst{
-        x_3
-        none
-        __u32
-        {
-          Identifier[not set]{x_12}
-        }
-      }
-    }
-    Return{
-      {
-        Identifier[not set]{x_3}
-      }
-    }
-  }
-  Function main -> __void
-  StageDecoration{vertex}
-  ()
-  {
-    VariableDeclStatement{
-      VariableConst{
-        x_15
-        none
-        __u32
-        {
-          Call[not set]{
-            Identifier[not set]{x_11}
-            (
-              Identifier[not set]{x_1}
-            )
-          }
-        }
-      }
-    }
-    Return{}
-  }
-})")) << module_str;
+  // This example is invalid because you can't pass pointer-to-Input
+  // as a function parameter.
+  EXPECT_FALSE(p->Parse());
+  EXPECT_THAT(p->error(),
+              HasSubstr("Invalid storage class for pointer operand 1"));
 }
 
 // Returns the start of a shader for testing InstanceIndex,
@@ -3506,11 +3531,11 @@
     OpFunctionEnd
  )";
   auto p = parser(test::Assemble(assembly));
-  // TODO(dneto): We can handle this if we make a shadow variable and mutate
-  // the parameter type.
-  ASSERT_FALSE(p->BuildAndParseInternalModule());
-  EXPECT_THAT(p->error(), HasSubstr("unhandled use of a pointer to the "
-                                    "InstanceIndex builtin, with ID: 1"));
+  // This example is invalid because you can't pass pointer-to-Input
+  // as a function parameter.
+  EXPECT_FALSE(p->Parse());
+  EXPECT_THAT(p->error(),
+              HasSubstr("Invalid storage class for pointer operand 1"));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_Direct) {
@@ -3656,71 +3681,11 @@
     OpFunctionEnd
  )";
   auto p = parser(test::Assemble(assembly));
-  // TODO(dneto): We can handle this if we make a shadow variable and mutate
-  // the parameter type.
-  ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
-  EXPECT_TRUE(p->error().empty());
-  const auto module_str = p->program().to_str();
-  // Correct declaration
-  EXPECT_THAT(module_str, HasSubstr(R"(
-  Variable{
-    Decorations{
-      BuiltinDecoration{instance_index}
-    }
-    x_1
-    in
-    __u32
-  })"));
-
-  // Correct bodies
-  EXPECT_THAT(module_str, HasSubstr(R"(
-  Function x_11 -> __u32
-  (
-    VariableConst{
-      x_12
-      none
-      __ptr_in__u32
-    }
-  )
-  {
-    VariableDeclStatement{
-      VariableConst{
-        x_3
-        none
-        __u32
-        {
-          Identifier[not set]{x_12}
-        }
-      }
-    }
-    Return{
-      {
-        Identifier[not set]{x_3}
-      }
-    }
-  }
-  Function main -> __void
-  StageDecoration{vertex}
-  ()
-  {
-    VariableDeclStatement{
-      VariableConst{
-        x_15
-        none
-        __u32
-        {
-          Call[not set]{
-            Identifier[not set]{x_11}
-            (
-              Identifier[not set]{x_1}
-            )
-          }
-        }
-      }
-    }
-    Return{}
-  }
-})")) << module_str;
+  // This example is invalid because you can't pass pointer-to-Input
+  // as a function parameter.
+  EXPECT_FALSE(p->Parse());
+  EXPECT_THAT(p->error(),
+              HasSubstr("Invalid storage class for pointer operand 1"));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, RegisterInputOutputVars) {
@@ -3733,6 +3698,10 @@
     OpEntryPoint GLCompute %1200 "w1200" %2 %15
     ; duplication is tolerated prior to SPIR-V 1.4
     OpEntryPoint GLCompute %1300 "w1300" %1 %15 %2 %1
+    OpExecutionMode %1000 LocalSize 1 1 1
+    OpExecutionMode %1100 LocalSize 1 1 1
+    OpExecutionMode %1200 LocalSize 1 1 1
+    OpExecutionMode %1300 LocalSize 1 1 1
 
 )" + CommonTypes() +
       R"(
@@ -3822,10 +3791,10 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, InputVarsConvertedToPrivate) {
-  const auto assembly = CommonTypes() + R"(
+  const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
      %ptr_in_uint = OpTypePointer Input %uint
      %1 = OpVariable %ptr_in_uint Input
-  )";
+  )" + MainBody();
   auto p = parser(test::Assemble(assembly));
 
   // TODO(crbug.com/tint/508): Remove this when everything is converted
@@ -3846,10 +3815,10 @@
 }
 
 TEST_F(SpvModuleScopeVarParserTest, OutputVarsConvertedToPrivate) {
-  const auto assembly = CommonTypes() + R"(
+  const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
      %ptr_out_uint = OpTypePointer Output %uint
      %1 = OpVariable %ptr_out_uint Output
-  )";
+  )" + MainBody();
   auto p = parser(test::Assemble(assembly));
 
   // TODO(crbug.com/tint/508): Remove this when everything is converted
diff --git a/src/reader/spirv/parser_impl_test_helper.cc b/src/reader/spirv/parser_impl_test_helper.cc
index f6bab45..158ecfd 100644
--- a/src/reader/spirv/parser_impl_test_helper.cc
+++ b/src/reader/spirv/parser_impl_test_helper.cc
@@ -27,8 +27,8 @@
     : impl_(input) {}
 
 ParserImplWrapperForTest::~ParserImplWrapperForTest() {
-  if (dump_successfully_converted_spirv_ && !impl_.spv_binary().empty() &&
-      impl_.success()) {
+  if (dump_successfully_converted_spirv_ && !deliberately_invalid_spirv_ &&
+      !impl_.spv_binary().empty() && impl_.success()) {
     std::string disassembly = Disassemble(impl_.spv_binary());
     std::cout << "BEGIN ConvertedOk:\n"
               << disassembly << "\nEND ConvertedOk" << std::endl;
diff --git a/src/reader/spirv/parser_impl_test_helper.h b/src/reader/spirv/parser_impl_test_helper.h
index 56a7366..0205cca 100644
--- a/src/reader/spirv/parser_impl_test_helper.h
+++ b/src/reader/spirv/parser_impl_test_helper.h
@@ -49,6 +49,7 @@
   static void DumpSuccessfullyConvertedSpirv() {
     dump_successfully_converted_spirv_ = true;
   }
+  void DeliberatelyInvalidSpirv() { deliberately_invalid_spirv_ = true; }
 
   // Returns a new function emitter for the given function ID.
   // Assumes ParserImpl::BuildInternalRepresentation has been run and
@@ -123,6 +124,9 @@
 
  private:
   ParserImpl impl_;
+  // When true, indicates the input SPIR-V module is expected to fail
+  // validation, but the SPIR-V reader parser is permissive and lets it through.
+  bool deliberately_invalid_spirv_ = false;
   static bool dump_successfully_converted_spirv_;
 };