spirv-diff: Allow no SpecId (#4904)

diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp
index 17750ff..7ed41de 100644
--- a/source/diff/diff.cpp
+++ b/source/diff/diff.cpp
@@ -1284,8 +1284,15 @@
     return src_spec_id == dst_spec_id;
   }
 
-  // There is no spec id, this is not valid.
-  assert(false && "Unreachable");
+  // There is no SpecId decoration, while not practical, still valid.
+  // SpecConstantOp don't have SpecId and can be matched by operands
+  if (src_inst->opcode() == SpvOpSpecConstantOp) {
+    if (src_inst->NumInOperandWords() == dst_inst->NumInOperandWords()) {
+      return DoOperandsMatch(src_inst, dst_inst, 0,
+                             src_inst->NumInOperandWords());
+    }
+  }
+
   return false;
 }
 
diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake
index e6d3645..6440d0b 100644
--- a/test/diff/diff_files/diff_test_files_autogen.cmake
+++ b/test/diff/diff_files/diff_test_files_autogen.cmake
@@ -41,5 +41,7 @@
 "diff_files/small_functions_small_diffs_autogen.cpp"
 "diff_files/spec_constant_array_size_autogen.cpp"
 "diff_files/spec_constant_composite_autogen.cpp"
+"diff_files/spec_constant_op_autogen.cpp"
+"diff_files/spec_constant_specid_autogen.cpp"
 "diff_files/unrelated_shaders_autogen.cpp"
 )
diff --git a/test/diff/diff_files/spec_constant_op_autogen.cpp b/test/diff/diff_files/spec_constant_op_autogen.cpp
new file mode 100644
index 0000000..e88e15b
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_op_autogen.cpp
@@ -0,0 +1,162 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests OpSpecConstantOp matching.
+constexpr char kSrc[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeVector %4 3
+          %6 = OpConstant %4 1
+          %7 = OpSpecConstantComposite %5 %6 %6 %6
+          %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+          %9 = OpSpecConstantOp %4 CompositeExtract %7 1
+         %10 = OpSpecConstantOp %4 CompositeExtract %7 0
+          %1 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+constexpr char kDst[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeVector %4 3
+          %6 = OpConstant %4 1
+          %7 = OpSpecConstantComposite %5 %6 %6 %6
+          %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+          %9 = OpSpecConstantOp %4 CompositeExtract %7 3
+         %10 = OpSpecConstantOp %4 IMul %8 %8
+          %1 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+TEST(DiffTest, SpecConstantOp) {
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 12
++; Bound: 14
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+-%9 = OpSpecConstantOp %4 CompositeExtract %7 1
+-%10 = OpSpecConstantOp %4 CompositeExtract %7 0
++%12 = OpSpecConstantOp %4 CompositeExtract %7 3
++%13 = OpSpecConstantOp %4 IMul %8 %8
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, SpecConstantOpNoDebug) {
+  constexpr char kSrcNoDebug[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeVector %4 3
+          %6 = OpConstant %4 1
+          %7 = OpSpecConstantComposite %5 %6 %6 %6
+          %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+          %9 = OpSpecConstantOp %4 CompositeExtract %7 1
+         %10 = OpSpecConstantOp %4 CompositeExtract %7 0
+          %1 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+  constexpr char kDstNoDebug[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeVector %4 3
+          %6 = OpConstant %4 1
+          %7 = OpSpecConstantComposite %5 %6 %6 %6
+          %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+          %9 = OpSpecConstantOp %4 CompositeExtract %7 3
+         %10 = OpSpecConstantOp %4 IMul %8 %8
+          %1 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 12
++; Bound: 14
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+-%9 = OpSpecConstantOp %4 CompositeExtract %7 1
+-%10 = OpSpecConstantOp %4 CompositeExtract %7 0
++%12 = OpSpecConstantOp %4 CompositeExtract %7 3
++%13 = OpSpecConstantOp %4 IMul %8 %8
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+}  // namespace
+}  // namespace diff
+}  // namespace spvtools
diff --git a/test/diff/diff_files/spec_constant_op_dst.spvasm b/test/diff/diff_files/spec_constant_op_dst.spvasm
new file mode 100644
index 0000000..b5b10e5
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_op_dst.spvasm
@@ -0,0 +1,17 @@
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeVector %4 3
+          %6 = OpConstant %4 1
+          %7 = OpSpecConstantComposite %5 %6 %6 %6
+          %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+          %9 = OpSpecConstantOp %4 CompositeExtract %7 3
+         %10 = OpSpecConstantOp %4 IMul %8 %8
+          %1 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_op_src.spvasm b/test/diff/diff_files/spec_constant_op_src.spvasm
new file mode 100644
index 0000000..09306f8
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_op_src.spvasm
@@ -0,0 +1,18 @@
+;; Tests OpSpecConstantOp matching.
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpTypeInt 32 0
+          %5 = OpTypeVector %4 3
+          %6 = OpConstant %4 1
+          %7 = OpSpecConstantComposite %5 %6 %6 %6
+          %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+          %9 = OpSpecConstantOp %4 CompositeExtract %7 1
+         %10 = OpSpecConstantOp %4 CompositeExtract %7 0
+          %1 = OpFunction %2 None %3
+         %11 = OpLabel
+               OpReturn
+               OpFunctionEnd
\ No newline at end of file
diff --git a/test/diff/diff_files/spec_constant_specid_autogen.cpp b/test/diff/diff_files/spec_constant_specid_autogen.cpp
new file mode 100644
index 0000000..240dc59
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_specid_autogen.cpp
@@ -0,0 +1,141 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests OpSpecConstantComposite matching.
+constexpr char kSrc[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %sc SpecId 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+         %sc = OpSpecConstant %uint 10
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd)";
+constexpr char kDst[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+         %ss = OpSpecConstant %uint 10
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+TEST(DiffTest, SpecConstantSpecid) {
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 8
++; Bound: 9
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+-OpDecorate %2 SpecId 0
+ %4 = OpTypeVoid
+ %3 = OpTypeFunction %4
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeVector %6 3
+-%2 = OpSpecConstant %6 10
++%8 = OpSpecConstant %6 10
+ %1 = OpFunction %4 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, SpecConstantSpecidNoDebug) {
+  constexpr char kSrcNoDebug[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %sc SpecId 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+         %sc = OpSpecConstant %uint 10
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  constexpr char kDstNoDebug[] = R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+         %ss = OpSpecConstant %uint 10
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 8
++; Bound: 9
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+-OpDecorate %2 SpecId 0
+ %4 = OpTypeVoid
+ %3 = OpTypeFunction %4
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeVector %6 3
+-%2 = OpSpecConstant %6 10
++%8 = OpSpecConstant %6 10
+ %1 = OpFunction %4 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+}  // namespace
+}  // namespace diff
+}  // namespace spvtools
diff --git a/test/diff/diff_files/spec_constant_specid_dst.spvasm b/test/diff/diff_files/spec_constant_specid_dst.spvasm
new file mode 100644
index 0000000..0e840ed
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_specid_dst.spvasm
@@ -0,0 +1,13 @@
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+         %ss = OpSpecConstant %uint 10
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_specid_src.spvasm b/test/diff/diff_files/spec_constant_specid_src.spvasm
new file mode 100644
index 0000000..7fdfeba
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_specid_src.spvasm
@@ -0,0 +1,15 @@
+;; Tests OpSpecConstantComposite matching.
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %sc SpecId 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+         %sc = OpSpecConstant %uint 10
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd