writer/spirv: Fix constant initialization

The code to auto-generate initializers for overridable pipeline
constants was in the non-const code path, so move it to the right
place and fix the tests that were wrongly testing non-const variables.

Bug: tint:254
Change-Id: Ifc96681492768ecf844f67e114377cc643ef7609
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/50040
Auto-Submit: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 27300ff..8a74ad5 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -688,8 +688,33 @@
 
   if (var->is_const()) {
     if (!var->has_constructor()) {
-      error_ = "missing constructor for constant";
-      return false;
+      // Constants must have an initializer unless they have an override
+      // decoration.
+      if (!ast::HasDecoration<ast::OverrideDecoration>(var->decorations())) {
+        error_ = "missing constructor for constant";
+        return false;
+      }
+
+      // SPIR-V requires specialization constants to have initializers.
+      if (sem->Type()->Is<sem::F32>()) {
+        ast::FloatLiteral l(ProgramID(), Source{}, 0.0f);
+        init_id = GenerateLiteralIfNeeded(var, &l);
+      } else if (sem->Type()->Is<sem::U32>()) {
+        ast::UintLiteral l(ProgramID(), Source{}, 0);
+        init_id = GenerateLiteralIfNeeded(var, &l);
+      } else if (sem->Type()->Is<sem::I32>()) {
+        ast::SintLiteral l(ProgramID(), Source{}, 0);
+        init_id = GenerateLiteralIfNeeded(var, &l);
+      } else if (sem->Type()->Is<sem::Bool>()) {
+        ast::BoolLiteral l(ProgramID(), Source{}, false);
+        init_id = GenerateLiteralIfNeeded(var, &l);
+      } else {
+        error_ = "invalid type for pipeline constant ID, must be scalar";
+        return false;
+      }
+      if (init_id == 0) {
+        return 0;
+      }
     }
     push_debug(spv::Op::OpName,
                {Operand::Int(init_id),
@@ -743,37 +768,10 @@
       }
     }
   } else if (!type_no_ac->Is<sem::Sampler>()) {
-    // Certain cases require us to generate a constructor value.
-    //
-    // 1- Pipeline constant IDs must be attached to the OpConstant, if we have a
-    //    variable with an override attribute that doesn't have a constructor we
-    //    make one
-    // 2- If we don't have a constructor and we're an Output or Private variable
-    //    then WGSL requires an initializer.
-    if (ast::HasDecoration<ast::OverrideDecoration>(var->decorations())) {
-      if (type_no_ac->Is<sem::F32>()) {
-        ast::FloatLiteral l(ProgramID(), Source{}, 0.0f);
-        init_id = GenerateLiteralIfNeeded(var, &l);
-      } else if (type_no_ac->Is<sem::U32>()) {
-        ast::UintLiteral l(ProgramID(), Source{}, 0);
-        init_id = GenerateLiteralIfNeeded(var, &l);
-      } else if (type_no_ac->Is<sem::I32>()) {
-        ast::SintLiteral l(ProgramID(), Source{}, 0);
-        init_id = GenerateLiteralIfNeeded(var, &l);
-      } else if (type_no_ac->Is<sem::Bool>()) {
-        ast::BoolLiteral l(ProgramID(), Source{}, false);
-        init_id = GenerateLiteralIfNeeded(var, &l);
-      } else {
-        error_ = "invalid type for pipeline constant ID, must be scalar";
-        return false;
-      }
-      if (init_id == 0) {
-        return 0;
-      }
-      ops.push_back(Operand::Int(init_id));
-    } else if (sem->StorageClass() == ast::StorageClass::kPrivate ||
-               sem->StorageClass() == ast::StorageClass::kNone ||
-               sem->StorageClass() == ast::StorageClass::kOutput) {
+    // If we don't have a constructor and we're an Output or Private variable,
+    // then WGSL requires that we zero-initialize.
+    if (sem->StorageClass() == ast::StorageClass::kPrivate ||
+        sem->StorageClass() == ast::StorageClass::kOutput) {
       init_id = GenerateConstantNullIfNeeded(type_no_ac);
       if (init_id == 0) {
         return 0;
diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc
index 51a5b9d..c264fc5 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -203,123 +203,111 @@
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_ConstantId_Bool) {
-  auto* v = Global("var", ty.bool_(), ast::StorageClass::kInput, Expr(true),
-                   ast::DecorationList{
-                       create<ast::OverrideDecoration>(1200),
-                   });
+TEST_F(BuilderTest, GlobalVar_Override_Bool) {
+  auto* v = GlobalConst("var", ty.bool_(), Expr(true),
+                        ast::DecorationList{
+                            create<ast::OverrideDecoration>(1200),
+                        });
 
   spirv::Builder& b = Build();
 
   EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "var"
+  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
 )");
   EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 1200
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
 %2 = OpSpecConstantTrue %1
-%4 = OpTypePointer Input %1
-%3 = OpVariable %4 Input %2
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_ConstantId_Bool_NoConstructor) {
-  auto* v = Global("var", ty.bool_(), ast::StorageClass::kInput, nullptr,
-                   ast::DecorationList{
-                       create<ast::OverrideDecoration>(1200),
-                   });
+TEST_F(BuilderTest, GlobalVar_Override_Bool_NoConstructor) {
+  auto* v = GlobalConst("var", ty.bool_(), nullptr,
+                        ast::DecorationList{
+                            create<ast::OverrideDecoration>(1200),
+                        });
 
   spirv::Builder& b = Build();
 
   EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
+  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
 )");
-  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 1200
+  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 1200
 )");
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeBool
-%2 = OpTypePointer Input %3
-%4 = OpSpecConstantFalse %3
-%1 = OpVariable %2 Input %4
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
+%2 = OpSpecConstantFalse %1
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar) {
-  auto* v = Global("var", ty.f32(), ast::StorageClass::kInput, Expr(2.f),
-                   ast::DecorationList{
-                       create<ast::OverrideDecoration>(0),
-                   });
+TEST_F(BuilderTest, GlobalVar_Override_Scalar) {
+  auto* v = GlobalConst("var", ty.f32(), Expr(2.f),
+                        ast::DecorationList{
+                            create<ast::OverrideDecoration>(0),
+                        });
 
   spirv::Builder& b = Build();
 
   EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "var"
+  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
 )");
   EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
 %2 = OpSpecConstant %1 2
-%4 = OpTypePointer Input %1
-%3 = OpVariable %4 Input %2
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar_F32_NoConstructor) {
-  auto* v = Global("var", ty.f32(), ast::StorageClass::kInput, nullptr,
-                   ast::DecorationList{
-                       create<ast::OverrideDecoration>(0),
-                   });
+TEST_F(BuilderTest, GlobalVar_Override_Scalar_F32_NoConstructor) {
+  auto* v = GlobalConst("var", ty.f32(), nullptr,
+                        ast::DecorationList{
+                            create<ast::OverrideDecoration>(0),
+                        });
 
   spirv::Builder& b = Build();
 
   EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
+  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
 )");
-  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 0
+  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
 )");
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
-%2 = OpTypePointer Input %3
-%4 = OpSpecConstant %3 0
-%1 = OpVariable %2 Input %4
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpSpecConstant %1 0
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar_I32_NoConstructor) {
-  auto* v = Global("var", ty.i32(), ast::StorageClass::kInput, nullptr,
-                   ast::DecorationList{
-                       create<ast::OverrideDecoration>(0),
-                   });
+TEST_F(BuilderTest, GlobalVar_Override_Scalar_I32_NoConstructor) {
+  auto* v = GlobalConst("var", ty.i32(), nullptr,
+                        ast::DecorationList{
+                            create<ast::OverrideDecoration>(0),
+                        });
 
   spirv::Builder& b = Build();
 
   EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
+  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
 )");
-  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 0
+  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
 )");
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
-%2 = OpTypePointer Input %3
-%4 = OpSpecConstant %3 0
-%1 = OpVariable %2 Input %4
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpSpecConstant %1 0
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar_U32_NoConstructor) {
-  auto* v = Global("var", ty.u32(), ast::StorageClass::kInput, nullptr,
-                   ast::DecorationList{
-                       create<ast::OverrideDecoration>(0),
-                   });
+TEST_F(BuilderTest, GlobalVar_Override_Scalar_U32_NoConstructor) {
+  auto* v = GlobalConst("var", ty.u32(), nullptr,
+                        ast::DecorationList{
+                            create<ast::OverrideDecoration>(0),
+                        });
 
   spirv::Builder& b = Build();
 
   EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
+  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
 )");
-  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 0
+  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
 )");
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 0
-%2 = OpTypePointer Input %3
-%4 = OpSpecConstant %3 0
-%1 = OpVariable %2 Input %4
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
+%2 = OpSpecConstant %1 0
 )");
 }