[spirv-reader][ir] Handle SpecConstants without a SpecId.

If an `OpSpecConstant*` does not have a `SpecId` decoration then we can
treat it as a constant. There is no-way from the SPIR-V side to override
the value, so we do not treat it as an override.

Bug: 398007970
Change-Id: If6f911634fe921f026a8f944c07e99350127ce94
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/230255
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index 0cdf89d..4c3fcc5 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -126,44 +126,53 @@
         return std::move(ir_);
     }
 
+    std::optional<uint16_t> GetSpecId(const spvtools::opt::Instruction& inst) {
+        auto decos =
+            spirv_context_->get_decoration_mgr()->GetDecorationsFor(inst.result_id(), true);
+        for (const auto* deco_inst : decos) {
+            TINT_ASSERT(deco_inst->opcode() == spv::Op::OpDecorate);
+
+            if (deco_inst->GetSingleWordInOperand(1) ==
+                static_cast<uint32_t>(spv::Decoration::SpecId)) {
+                return {static_cast<uint16_t>(deco_inst->GetSingleWordInOperand(2))};
+            }
+        }
+        return std::nullopt;
+    }
+
     // Generate a module-scope const declaration for each instruction
     // that is OpSpecConstantTrue, OpSpecConstantFalse, or OpSpecConstant.
     void EmitSpecConstants() {
         for (auto& inst : spirv_context_->types_values()) {
-            core::ir::Override* override_ = nullptr;
+            core::ir::Value* value = nullptr;
+            std::optional<uint16_t> spec_id = std::nullopt;
             switch (inst.opcode()) {
                 case spv::Op::OpSpecConstantTrue:
-                case spv::Op::OpSpecConstantFalse:
-                    override_ = b_.Override(Type(inst.type_id()));
-                    override_->SetInitializer(
-                        b_.Value(inst.opcode() == spv::Op::OpSpecConstantTrue));
+                case spv::Op::OpSpecConstantFalse: {
+                    value = b_.Value(inst.opcode() == spv::Op::OpSpecConstantTrue);
+                    spec_id = GetSpecId(inst);
                     break;
+                }
                 default:
-                    break;
+                    continue;
             }
-            if (!override_) {
+
+            // No spec_id means treat this as a constant.
+            if (!spec_id.has_value()) {
+                AddValue(inst.result_id(), value);
                 continue;
             }
 
+            auto* override_ = b_.Override(Type(inst.type_id()));
+            override_->SetInitializer(value);
+            override_->SetOverrideId(OverrideId{spec_id.value()});
+
             Emit(override_, inst.result_id());
 
             Symbol name = GetSymbolFor(inst.result_id());
             if (name.IsValid()) {
                 ir_.SetName(override_, name);
             }
-
-            auto decos =
-                spirv_context_->get_decoration_mgr()->GetDecorationsFor(inst.result_id(), true);
-            for (const auto* deco_inst : decos) {
-                TINT_ASSERT(deco_inst->opcode() == spv::Op::OpDecorate);
-
-                if (deco_inst->GetSingleWordInOperand(1) ==
-                    static_cast<uint32_t>(spv::Decoration::SpecId)) {
-                    const uint16_t id = static_cast<uint16_t>(deco_inst->GetSingleWordInOperand(2));
-                    override_->SetOverrideId(OverrideId{id});
-                    break;
-                }
-            }
         }
     }
 
diff --git a/src/tint/lang/spirv/reader/parser/var_test.cc b/src/tint/lang/spirv/reader/parser/var_test.cc
index 88c141a..e37e7e8 100644
--- a/src/tint/lang/spirv/reader/parser/var_test.cc
+++ b/src/tint/lang/spirv/reader/parser/var_test.cc
@@ -642,6 +642,35 @@
 )");
 }
 
+TEST_F(SpirvParserTest, Var_OpSpecConstantTrue_NoSpecId) {
+    EXPECT_IR(R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %c "myconst"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+        %f32 = OpTypeFloat 32
+      %vec4f = OpTypeVector %f32 4
+          %c = OpSpecConstantTrue %bool
+     %voidfn = OpTypeFunction %void
+       %main = OpFunction %void None %voidfn
+ %main_entry = OpLabel
+          %b = OpLogicalAnd %bool %c %c
+               OpReturn
+               OpFunctionEnd
+)",
+              R"(
+%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
+  $B1: {
+    %2:bool = and true, true
+    ret
+  }
+}
+)");
+}
+
 TEST_F(SpirvParserTest, Var_OpSpecConstantFalse) {
     EXPECT_IR(R"(
                OpCapability Shader
@@ -676,5 +705,34 @@
 )");
 }
 
+TEST_F(SpirvParserTest, Var_OpSpecConstantFalse_NoSpecId) {
+    EXPECT_IR(R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %c "myconst"
+       %void = OpTypeVoid
+       %bool = OpTypeBool
+        %f32 = OpTypeFloat 32
+      %vec4f = OpTypeVector %f32 4
+          %c = OpSpecConstantFalse %bool
+     %voidfn = OpTypeFunction %void
+       %main = OpFunction %void None %voidfn
+ %main_entry = OpLabel
+          %b = OpLogicalAnd %bool %c %c
+               OpReturn
+               OpFunctionEnd
+)",
+              R"(
+%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
+  $B1: {
+    %2:bool = and false, false
+    ret
+  }
+}
+)");
+}
+
 }  // namespace
 }  // namespace tint::spirv::reader