spirv-fuzz: Improve TransformationAddBitInstructionSynonym to check integer signedness (#4312)

Fixes #4170, by checking the signedness of bitwise operands in
TransformationAddBitInstructionSynonym, to avoid an "Expected Base
Type to be equal to Result Type" validation error.
diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
index b2edc19..78abf5b 100644
--- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
@@ -45,22 +45,11 @@
           continue;
         }
 
-        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
-        //  Right now we only support certain operations. When this issue is
-        //  addressed the following conditional can use the function
-        //  |spvOpcodeIsBit|.
-        if (instruction.opcode() != SpvOpBitwiseOr &&
-            instruction.opcode() != SpvOpBitwiseXor &&
-            instruction.opcode() != SpvOpBitwiseAnd &&
-            instruction.opcode() != SpvOpNot) {
-          continue;
-        }
-
-        // Right now, only integer operands are supported.
-        if (GetIRContext()
-                ->get_type_mgr()
-                ->GetType(instruction.type_id())
-                ->AsVector()) {
+        // Make sure fuzzer never applies a transformation to a bitwise
+        // instruction with differently signed operands, only integer operands
+        // are supported and bitwise operations are supported only.
+        if (!TransformationAddBitInstructionSynonym::IsInstructionSupported(
+                GetIRContext(), &instruction)) {
           continue;
         }
 
diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
index d232e13..636c0a3 100644
--- a/source/fuzz/transformation_add_bit_instruction_synonym.cpp
+++ b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
@@ -39,20 +39,9 @@
   auto instruction =
       ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
 
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
-  //  Right now we only support certain operations. When this issue is addressed
-  //  the following conditional can use the function |spvOpcodeIsBit|.
-  // |instruction| must be defined and must be a supported bit instruction.
-  if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
-                       instruction->opcode() != SpvOpBitwiseXor &&
-                       instruction->opcode() != SpvOpBitwiseAnd &&
-                       instruction->opcode() != SpvOpNot)) {
-    return false;
-  }
-
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792):
-  //  Right now, only integer operands are supported.
-  if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) {
+  // Checks on: only integer operands are supported, instructions are bitwise
+  // operations only. Signedness of the operands must be the same.
+  if (!IsInstructionSupported(ir_context, instruction)) {
     return false;
   }
 
@@ -111,6 +100,65 @@
   }
 }
 
+bool TransformationAddBitInstructionSynonym::IsInstructionSupported(
+    opt::IRContext* ir_context, opt::Instruction* instruction) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
+  //  Right now we only support certain operations. When this issue is addressed
+  //  the following conditional can use the function |spvOpcodeIsBit|.
+  // |instruction| must be defined and must be a supported bit instruction.
+  if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
+                       instruction->opcode() != SpvOpBitwiseXor &&
+                       instruction->opcode() != SpvOpBitwiseAnd &&
+                       instruction->opcode() != SpvOpNot)) {
+    return false;
+  }
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792):
+  //  Right now, only integer operands are supported.
+  if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) {
+    return false;
+  }
+
+  if (instruction->opcode() == SpvOpNot) {
+    auto operand = instruction->GetInOperand(0).words[0];
+    auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand);
+    auto operand_type =
+        ir_context->get_type_mgr()->GetType(operand_inst->type_id());
+    auto operand_sign = operand_type->AsInteger()->IsSigned();
+
+    auto type_id_sign = ir_context->get_type_mgr()
+                            ->GetType(instruction->type_id())
+                            ->AsInteger()
+                            ->IsSigned();
+
+    return operand_sign == type_id_sign;
+
+  } else {
+    // Other BitWise operations that takes two operands.
+    auto first_operand = instruction->GetInOperand(0).words[0];
+    auto first_operand_inst =
+        ir_context->get_def_use_mgr()->GetDef(first_operand);
+    auto first_operand_type =
+        ir_context->get_type_mgr()->GetType(first_operand_inst->type_id());
+    auto first_operand_sign = first_operand_type->AsInteger()->IsSigned();
+
+    auto second_operand = instruction->GetInOperand(1).words[0];
+    auto second_operand_inst =
+        ir_context->get_def_use_mgr()->GetDef(second_operand);
+    auto second_operand_type =
+        ir_context->get_type_mgr()->GetType(second_operand_inst->type_id());
+    auto second_operand_sign = second_operand_type->AsInteger()->IsSigned();
+
+    auto type_id_sign = ir_context->get_type_mgr()
+                            ->GetType(instruction->type_id())
+                            ->AsInteger()
+                            ->IsSigned();
+
+    return first_operand_sign == second_operand_sign &&
+           first_operand_sign == type_id_sign;
+  }
+}
+
 protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage()
     const {
   protobufs::Transformation result;
diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.h b/source/fuzz/transformation_add_bit_instruction_synonym.h
index ad9d32d..40b947a 100644
--- a/source/fuzz/transformation_add_bit_instruction_synonym.h
+++ b/source/fuzz/transformation_add_bit_instruction_synonym.h
@@ -128,6 +128,14 @@
   static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context,
                                           opt::Instruction* bit_instruction);
 
+  //   Returns true if:
+  // - A |bit_instruction| is one of OpBitwiseOr, OpBitwiseAnd, OpBitwiseXor or
+  // OpNot.
+  // - |bit_instruction|'s operands are scalars.
+  // - The operands have the same signedness.
+  static bool IsInstructionSupported(opt::IRContext* ir_context,
+                                     opt::Instruction* instruction);
+
  private:
   protobufs::TransformationAddBitInstructionSynonym message_;
 
diff --git a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
index ccfbcf5..d6b2ce5 100644
--- a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
+++ b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
@@ -937,11 +937,7 @@
       MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {})));
 }
 
-TEST(TransformationAddBitInstructionSynonymTest, DISABLED_DifferentSingedness) {
-  // This test will fail due to a bug in the transformation. The reason is that
-  // OpNot supports its operand and result type having different signedness.
-  // OpBitFieldUExtract and OpBitFieldInsert, however, don't support this
-  // (these instructions are added by the transformation).
+TEST(TransformationAddBitInstructionSynonymTest, DifferentSingedness) {
   std::string reference_shader = R"(
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
@@ -987,11 +983,14 @@
          %34 = OpConstant %2 29
          %35 = OpConstant %2 30
          %36 = OpConstant %2 31
+         %45 = OpConstant %200 32
 
 ; main function
          %37 = OpFunction %3 None %4
          %38 = OpLabel
          %39 = OpNot %200 %5 ; bit instruction
+         %40 = OpBitwiseOr %200 %6 %45  ; bit instruction
+         %41 = OpBitwiseAnd %2 %5 %6 ; bit instruction
                OpReturn
                OpFunctionEnd
   )";
@@ -1006,17 +1005,61 @@
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
 
-  // Adds OpNot synonym.
+  // Invalid because the sign of id 200 result is not equal to the sign of id 5
+  // operand in OpNot.
   auto transformation = TransformationAddBitInstructionSynonym(
-      39, {40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,
-           54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,
-           68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,
-           82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
-           96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
-           110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
-           124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134});
+      39, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+           313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+           326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
+           339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351,
+           352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+           365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
+           378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390,
+           391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
+           404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416,
+           417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427});
   ASSERT_FALSE(
       transformation.IsApplicable(context.get(), transformation_context));
+
+  // Invalid because the sign of two operands not the same and the first operand
+  // sign not equal the result sign in OpBitwiseOr.
+  transformation = TransformationAddBitInstructionSynonym(
+      40, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+           313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+           326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
+           339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351,
+           352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+           365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
+           378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390,
+           391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
+           404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416,
+           417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427});
+  ASSERT_FALSE(
+      transformation.IsApplicable(context.get(), transformation_context));
+
+  // Successful transformation
+  {
+    // Instruction operands are the same and it's equal with the result sign in
+    // OpBitwiseAnd bitwise operation.
+    transformation = TransformationAddBitInstructionSynonym(
+        41, {46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,
+             59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
+             72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,
+             85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
+             98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+             111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
+             124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+             137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+             150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+             163, 164, 165, 166, 167, 168, 169, 170, 171, 172});
+    ASSERT_TRUE(
+        transformation.IsApplicable(context.get(), transformation_context));
+
+    ApplyAndCheckFreshIds(transformation, context.get(),
+                          &transformation_context);
+    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+        context.get(), validator_options, kConsoleMessageConsumer));
+  }
 }
 
 }  // namespace