spirv-fuzz: Support bitwise or and xor in TransformationAddBitInstructionSynonym (#4310)

Fixes #4172.
diff --git a/source/fuzz/fuzzer_pass_add_synonyms.cpp b/source/fuzz/fuzzer_pass_add_synonyms.cpp
index 2a85e52..fd866f9 100644
--- a/source/fuzz/fuzzer_pass_add_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_synonyms.cpp
@@ -79,6 +79,8 @@
           case protobufs::TransformationAddSynonym::ADD_ZERO:
           case protobufs::TransformationAddSynonym::SUB_ZERO:
           case protobufs::TransformationAddSynonym::LOGICAL_OR:
+          case protobufs::TransformationAddSynonym::BITWISE_OR:
+          case protobufs::TransformationAddSynonym::BITWISE_XOR:
             // Create a zero constant to be used as an operand of the synonymous
             // instruction.
             FindOrCreateZeroConstant(existing_synonym->type_id(), false);
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index eadf824..657e807 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -1136,6 +1136,14 @@
     // New synonym is derived by applying OpLogicalAnd to |result_id| with the second
     // operand being 'true'.
     LOGICAL_AND = 5;
+
+    // New synonym is derived by applying OpBitwiseOr to |result_id| with the second
+    // operand being 0 taken with the same bit length as |result_id|
+    BITWISE_OR = 6;
+
+    // New synonym is derived by applying OpBitwiseXor to |result_id| with the second
+    // operand being 0 taken with the same bit length as |result_id|
+    BITWISE_XOR = 7;
   }
 
   // Type of the synonym to create. See SynonymType for more details.
diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp
index 40768e2..a1949fb 100644
--- a/source/fuzz/transformation_add_synonym.cpp
+++ b/source/fuzz/transformation_add_synonym.cpp
@@ -170,6 +170,18 @@
 
       return type->AsInteger() || type->AsFloat();
     }
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_XOR: {
+      // The instruction must be either an integer or a vector of integers.
+      const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+      assert(type && "Instruction's result id is invalid");
+
+      if (const auto* vector = type->AsVector()) {
+        return vector->element_type()->AsInteger();
+      }
+
+      return type->AsInteger();
+    }
     case protobufs::TransformationAddSynonym::COPY_OBJECT:
       // All checks for OpCopyObject are handled by
       // fuzzerutil::CanMakeSynonymOf.
@@ -195,68 +207,56 @@
   auto synonym_type_id =
       fuzzerutil::GetTypeId(ir_context, message_.result_id());
   assert(synonym_type_id && "Synonym has invalid type id");
+  auto opcode = SpvOpNop;
+  const auto* synonym_type =
+      ir_context->get_type_mgr()->GetType(synonym_type_id);
+  assert(synonym_type && "Synonym has invalid type");
+
+  auto is_integral = (synonym_type->AsVector() &&
+                      synonym_type->AsVector()->element_type()->AsInteger()) ||
+                     synonym_type->AsInteger();
 
   switch (message_.synonym_type()) {
     case protobufs::TransformationAddSynonym::SUB_ZERO:
+      opcode = is_integral ? SpvOpISub : SpvOpFSub;
+      break;
     case protobufs::TransformationAddSynonym::MUL_ONE:
-    case protobufs::TransformationAddSynonym::ADD_ZERO: {
-      const auto* synonym_type =
-          ir_context->get_type_mgr()->GetType(synonym_type_id);
-      assert(synonym_type && "Synonym has invalid type");
+      opcode = is_integral ? SpvOpIMul : SpvOpFMul;
+      break;
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+      opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
+      break;
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+      opcode = SpvOpLogicalOr;
+      break;
+    case protobufs::TransformationAddSynonym::LOGICAL_AND:
+      opcode = SpvOpLogicalAnd;
+      break;
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+      opcode = SpvOpBitwiseOr;
+      break;
+    case protobufs::TransformationAddSynonym::BITWISE_XOR:
+      opcode = SpvOpBitwiseXor;
+      break;
 
-      // Compute instruction's opcode based on the type of the operand.
-      // We have already checked that the operand is either a scalar or a vector
-      // of either integers or floats.
-      auto is_integral =
-          (synonym_type->AsVector() &&
-           synonym_type->AsVector()->element_type()->AsInteger()) ||
-          synonym_type->AsInteger();
-      auto opcode = SpvOpNop;
-      switch (message_.synonym_type()) {
-        case protobufs::TransformationAddSynonym::SUB_ZERO:
-          opcode = is_integral ? SpvOpISub : SpvOpFSub;
-          break;
-        case protobufs::TransformationAddSynonym::MUL_ONE:
-          opcode = is_integral ? SpvOpIMul : SpvOpFMul;
-          break;
-        case protobufs::TransformationAddSynonym::ADD_ZERO:
-          opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
-          break;
-        default:
-          assert(false && "Unreachable");
-          break;
-      }
-
-      return MakeUnique<opt::Instruction>(
-          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
-          opt::Instruction::OperandList{
-              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
-              {SPV_OPERAND_TYPE_ID,
-               {MaybeGetConstantId(ir_context, transformation_context)}}});
-    }
     case protobufs::TransformationAddSynonym::COPY_OBJECT:
       return MakeUnique<opt::Instruction>(
           ir_context, SpvOpCopyObject, synonym_type_id,
           message_.synonym_fresh_id(),
           opt::Instruction::OperandList{
               {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
-    case protobufs::TransformationAddSynonym::LOGICAL_OR:
-    case protobufs::TransformationAddSynonym::LOGICAL_AND: {
-      auto opcode = message_.synonym_type() ==
-                            protobufs::TransformationAddSynonym::LOGICAL_OR
-                        ? SpvOpLogicalOr
-                        : SpvOpLogicalAnd;
-      return MakeUnique<opt::Instruction>(
-          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
-          opt::Instruction::OperandList{
-              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
-              {SPV_OPERAND_TYPE_ID,
-               {MaybeGetConstantId(ir_context, transformation_context)}}});
-    }
+
     default:
       assert(false && "Unhandled synonym type");
       return nullptr;
   }
+
+  return MakeUnique<opt::Instruction>(
+      ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+          {SPV_OPERAND_TYPE_ID,
+           {MaybeGetConstantId(ir_context, transformation_context)}}});
 }
 
 uint32_t TransformationAddSynonym::MaybeGetConstantId(
@@ -273,6 +273,8 @@
     case protobufs::TransformationAddSynonym::ADD_ZERO:
     case protobufs::TransformationAddSynonym::SUB_ZERO:
     case protobufs::TransformationAddSynonym::LOGICAL_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_XOR:
       return fuzzerutil::MaybeGetZeroConstant(
           ir_context, transformation_context, synonym_type_id, false);
     case protobufs::TransformationAddSynonym::MUL_ONE:
@@ -319,6 +321,8 @@
     case protobufs::TransformationAddSynonym::LOGICAL_OR:
     case protobufs::TransformationAddSynonym::MUL_ONE:
     case protobufs::TransformationAddSynonym::LOGICAL_AND:
+    case protobufs::TransformationAddSynonym::BITWISE_OR:
+    case protobufs::TransformationAddSynonym::BITWISE_XOR:
       return true;
     default:
       return false;
diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp
index 3803fa3..314b003 100644
--- a/test/fuzz/transformation_add_synonym_test.cpp
+++ b/test/fuzz/transformation_add_synonym_test.cpp
@@ -197,6 +197,7 @@
          %37 = OpTypeVector %36 2
          %38 = OpConstantTrue %36
          %39 = OpConstantComposite %37 %38 %38
+         %40 = OpConstant %6 37
           %4 = OpFunction %2 None %3
           %5 = OpLabel
                OpReturn
@@ -249,6 +250,29 @@
       ++fresh_id;
     }
   }
+  {
+    TransformationAddSynonym transformation(
+        40, protobufs::TransformationAddSynonym::BITWISE_OR, fresh_id,
+        insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {})));
+    ++fresh_id;
+  }
+  {
+    TransformationAddSynonym transformation(
+        40, protobufs::TransformationAddSynonym::BITWISE_XOR, fresh_id,
+        insert_before);
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {})));
+  }
 
   std::string expected_shader = R"(
                OpCapability Shader
@@ -289,6 +313,7 @@
          %37 = OpTypeVector %36 2
          %38 = OpConstantTrue %36
          %39 = OpConstantComposite %37 %38 %38
+         %40 = OpConstant %6 37
           %4 = OpFunction %2 None %3
           %5 = OpLabel
          %50 = OpIAdd %6 %9 %7
@@ -303,6 +328,8 @@
          %59 = OpFMul %14 %17 %16
          %60 = OpFMul %18 %23 %20
          %61 = OpIMul %24 %29 %26
+         %62 = OpBitwiseOr %6 %40 %7
+         %63 = OpBitwiseXor %6 %40 %7
                OpReturn
                OpFunctionEnd
   )";