spirv-fuzz: TransformationReplaceAddSubMulWithCarryingExtended (#3598)
Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul with
OpUMulExtended or OpSMulExtended and stores the result into a fresh_id
representing a structure. Extracts the first element of the result into
the original result_id. This value is the same as the result of the
original instruction.
Fixes #3577
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 4564fcf..a056630 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -76,6 +76,7 @@
fuzzer_pass_permute_instructions.h
fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
+ fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
fuzzer_pass_replace_copy_memories_with_loads_stores.h
fuzzer_pass_replace_copy_objects_with_stores_loads.h
fuzzer_pass_replace_linear_algebra_instructions.h
@@ -143,6 +144,7 @@
transformation_permute_phi_operands.h
transformation_push_id_through_variable.h
transformation_record_synonymous_constants.h
+ transformation_replace_add_sub_mul_with_carrying_extended.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_copy_memory_with_load_store.h
@@ -211,6 +213,7 @@
fuzzer_pass_permute_instructions.cpp
fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
+ fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
@@ -277,6 +280,7 @@
transformation_permute_phi_operands.cpp
transformation_push_id_through_variable.cpp
transformation_record_synonymous_constants.cpp
+ transformation_replace_add_sub_mul_with_carrying_extended.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_copy_memory_with_load_store.cpp
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index c1e456c..9ba5442 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -82,6 +82,8 @@
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
+const std::pair<uint32_t, uint32_t>
+ kChanceOfReplacingAddSubMulWithCarryingExtended = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
{20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
@@ -227,6 +229,8 @@
ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
chance_of_pushing_id_through_variable_ =
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
+ chance_of_replacing_add_sub_mul_with_carrying_extended_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingAddSubMulWithCarryingExtended);
chance_of_replacing_copy_memory_with_load_store_ =
ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
chance_of_replacing_copyobject_with_store_load_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 10f3630..f4f9cf8 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -221,6 +221,9 @@
uint32_t GetChanceOfPushingIdThroughVariable() {
return chance_of_pushing_id_through_variable_;
}
+ uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() {
+ return chance_of_replacing_add_sub_mul_with_carrying_extended_;
+ }
uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
return chance_of_replacing_copy_memory_with_load_store_;
}
@@ -375,6 +378,7 @@
uint32_t chance_of_permuting_parameters_;
uint32_t chance_of_permuting_phi_operands_;
uint32_t chance_of_pushing_id_through_variable_;
+ uint32_t chance_of_replacing_add_sub_mul_with_carrying_extended_;
uint32_t chance_of_replacing_copy_memory_with_load_store_;
uint32_t chance_of_replacing_copyobject_with_store_load_;
uint32_t chance_of_replacing_id_with_synonym_;
diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
new file mode 100644
index 0000000..d506de6
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
@@ -0,0 +1,76 @@
+// Copyright (c) 2020 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 "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
+} // namespace
+
+FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
+ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
+ ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default;
+
+void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
+ for (auto& function : *GetIRContext()->module()) {
+ for (auto& block : function) {
+ for (auto& instruction : block) {
+ // Randomly decide whether to apply the transformation.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfReplacingAddSubMulWithCarryingExtended())) {
+ continue;
+ }
+
+ // Check if the transformation can be applied to this instruction.
+ if (!TransformationReplaceAddSubMulWithCarryingExtended::
+ IsInstructionSuitable(GetIRContext(), instruction)) {
+ continue;
+ }
+
+ // Get the operand type id. We know that both operands have the same
+ // type.
+ uint32_t operand_type_id =
+ GetIRContext()
+ ->get_def_use_mgr()
+ ->GetDef(instruction.GetSingleWordInOperand(
+ kArithmeticInstructionIndexLeftInOperand))
+ ->type_id();
+
+ // Ensure the required struct type exists. The struct type is based on
+ // the operand type.
+ FindOrCreateStructType({operand_type_id, operand_type_id});
+
+ ApplyTransformation(TransformationReplaceAddSubMulWithCarryingExtended(
+ GetFuzzerContext()->GetFreshId(), instruction.result_id()));
+ }
+ }
+ }
+}
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
new file mode 100644
index 0000000..f224d2b
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 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.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that replaces instructions OpIAdd, OpISub, OpIMul with pairs of
+// instructions. The first one (OpIAddCarry, OpISubBorrow, OpUMulExtended,
+// OpSMulExtended) computes the result into a struct. The second one extracts
+// the appropriate component from the struct to yield the original result.
+class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass {
+ public:
+ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index e154f2a..45414f6 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -410,6 +410,7 @@
TransformationAddLoopPreheader add_loop_preheader = 63;
TransformationMoveInstructionDown move_instruction_down = 64;
TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65;
+ TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66;
// Add additional option using the next available number.
}
}
@@ -1285,6 +1286,24 @@
}
+message TransformationReplaceAddSubMulWithCarryingExtended {
+
+ // Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul
+ // with OpUMulExtended or OpSMulExtended (depending on the signedness
+ // of the operands) and stores the result into a |struct_fresh_id|.
+ // In the original instruction the result type id and the type ids of
+ // the operands must be the same. Then the transformation extracts
+ // the first element of the result into the original |result_id|.
+ // This value is the same as the result of the original instruction.
+
+ // The fresh id of the intermediate result.
+ uint32 struct_fresh_id = 1;
+
+ // The result id of the original instruction.
+ uint32 result_id = 2;
+
+}
+
message TransformationReplaceParameterWithGlobal {
// Removes parameter with result id |parameter_id| from its function
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 0824d4f..727309a 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -63,6 +63,7 @@
#include "source/fuzz/transformation_permute_phi_operands.h"
#include "source/fuzz/transformation_push_id_through_variable.h"
#include "source/fuzz/transformation_record_synonymous_constants.h"
+#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
@@ -221,9 +222,9 @@
return MakeUnique<TransformationRecordSynonymousConstants>(
message.record_synonymous_constants());
case protobufs::Transformation::TransformationCase::
- kReplaceParameterWithGlobal:
- return MakeUnique<TransformationReplaceParameterWithGlobal>(
- message.replace_parameter_with_global());
+ kReplaceAddSubMulWithCarryingExtended:
+ return MakeUnique<TransformationReplaceAddSubMulWithCarryingExtended>(
+ message.replace_add_sub_mul_with_carrying_extended());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
@@ -252,6 +253,10 @@
return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
message.replace_load_store_with_copy_memory());
case protobufs::Transformation::TransformationCase::
+ kReplaceParameterWithGlobal:
+ return MakeUnique<TransformationReplaceParameterWithGlobal>(
+ message.replace_parameter_with_global());
+ case protobufs::Transformation::TransformationCase::
kReplaceParamsWithStruct:
return MakeUnique<TransformationReplaceParamsWithStruct>(
message.replace_params_with_struct());
diff --git a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
new file mode 100644
index 0000000..ea84cf2
--- /dev/null
+++ b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
@@ -0,0 +1,232 @@
+// Copyright (c) 2020 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 "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kOpCompositeExtractIndexLowOrderBits = 0;
+const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
+const uint32_t kArithmeticInstructionIndexRightInOperand = 1;
+} // namespace
+
+TransformationReplaceAddSubMulWithCarryingExtended::
+ TransformationReplaceAddSubMulWithCarryingExtended(
+ const spvtools::fuzz::protobufs::
+ TransformationReplaceAddSubMulWithCarryingExtended& message)
+ : message_(message) {}
+
+TransformationReplaceAddSubMulWithCarryingExtended::
+ TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id,
+ uint32_t result_id) {
+ message_.set_struct_fresh_id(struct_fresh_id);
+ message_.set_result_id(result_id);
+}
+
+bool TransformationReplaceAddSubMulWithCarryingExtended::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // |message_.struct_fresh_id| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id())) {
+ return false;
+ }
+
+ // |message_.result_id| must refer to a suitable OpIAdd, OpISub or OpIMul
+ // instruction. The instruction must be defined.
+ auto instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+ if (instruction == nullptr) {
+ return false;
+ }
+ if (!TransformationReplaceAddSubMulWithCarryingExtended::
+ IsInstructionSuitable(ir_context, *instruction)) {
+ return false;
+ }
+
+ // The struct type for holding the intermediate result must exist in the
+ // module. The struct type is based on the operand type.
+ uint32_t operand_type_id = ir_context->get_def_use_mgr()
+ ->GetDef(instruction->GetSingleWordInOperand(
+ kArithmeticInstructionIndexLeftInOperand))
+ ->type_id();
+
+ uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
+ ir_context, {operand_type_id, operand_type_id});
+ if (struct_type_id == 0) {
+ return false;
+ }
+ return true;
+}
+
+void TransformationReplaceAddSubMulWithCarryingExtended::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ // |message_.struct_fresh_id| must be fresh.
+ assert(fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id()) &&
+ "|message_.struct_fresh_id| must be fresh");
+
+ // Get the signedness of an operand if it is an int or the signedness of a
+ // component if it is a vector.
+ auto type_id =
+ ir_context->get_def_use_mgr()->GetDef(message_.result_id())->type_id();
+ auto type = ir_context->get_type_mgr()->GetType(type_id);
+ bool operand_is_signed;
+ if (type->kind() == opt::analysis::Type::kVector) {
+ auto operand_type = type->AsVector()->element_type();
+ operand_is_signed = operand_type->AsInteger()->IsSigned();
+ } else {
+ operand_is_signed = type->AsInteger()->IsSigned();
+ }
+
+ auto original_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.struct_fresh_id());
+
+ // Determine the opcode of the new instruction that computes the result into a
+ // struct.
+ SpvOp new_instruction_opcode;
+
+ switch (original_instruction->opcode()) {
+ case SpvOpIAdd:
+ new_instruction_opcode = SpvOpIAddCarry;
+ break;
+ case SpvOpISub:
+ new_instruction_opcode = SpvOpISubBorrow;
+ break;
+ case SpvOpIMul:
+ if (!operand_is_signed) {
+ new_instruction_opcode = SpvOpUMulExtended;
+ } else {
+ new_instruction_opcode = SpvOpSMulExtended;
+ }
+ break;
+ default:
+ assert(false && "The instruction has an unsupported opcode.");
+ return;
+ }
+ // Get the type of struct type id holding the intermediate result based on the
+ // operand type.
+ uint32_t operand_type_id =
+ ir_context->get_def_use_mgr()
+ ->GetDef(original_instruction->GetSingleWordInOperand(
+ kArithmeticInstructionIndexLeftInOperand))
+ ->type_id();
+
+ uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
+ ir_context, {operand_type_id, operand_type_id});
+ // Avoid unused variables in release mode.
+ (void)struct_type_id;
+ assert(struct_type_id && "The struct type must exist in the module.");
+
+ // Insert the new instruction that computes the result into a struct before
+ // the |original_instruction|.
+ original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, new_instruction_opcode, struct_type_id,
+ message_.struct_fresh_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID,
+ {original_instruction->GetSingleWordInOperand(
+ kArithmeticInstructionIndexLeftInOperand)}},
+ {SPV_OPERAND_TYPE_ID,
+ {original_instruction->GetSingleWordInOperand(
+ kArithmeticInstructionIndexRightInOperand)}}})));
+
+ // Insert the OpCompositeExtract after the added instruction. This instruction
+ // takes the first component of the struct which represents low-order bits of
+ // the operation. This is the original result.
+ original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract, original_instruction->type_id(),
+ message_.result_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.struct_fresh_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER,
+ {kOpCompositeExtractIndexLowOrderBits}}})));
+
+ // Remove the original instruction.
+ ir_context->KillInst(original_instruction);
+
+ // We have modified the module so most analyzes are now invalid.
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+bool TransformationReplaceAddSubMulWithCarryingExtended::IsInstructionSuitable(
+ opt::IRContext* ir_context, const opt::Instruction& instruction) {
+ auto instruction_opcode = instruction.opcode();
+
+ // Only instructions OpIAdd, OpISub, OpIMul are supported.
+ switch (instruction_opcode) {
+ case SpvOpIAdd:
+ case SpvOpISub:
+ case SpvOpIMul:
+ break;
+ default:
+ return false;
+ }
+ uint32_t operand_1_type_id =
+ ir_context->get_def_use_mgr()
+ ->GetDef(instruction.GetSingleWordInOperand(
+ kArithmeticInstructionIndexLeftInOperand))
+ ->type_id();
+
+ uint32_t operand_2_type_id =
+ ir_context->get_def_use_mgr()
+ ->GetDef(instruction.GetSingleWordInOperand(
+ kArithmeticInstructionIndexRightInOperand))
+ ->type_id();
+
+ uint32_t result_type_id = instruction.type_id();
+
+ // Both type ids of the operands and the result type ids must be equal.
+ if (operand_1_type_id != operand_2_type_id) {
+ return false;
+ }
+ if (operand_2_type_id != result_type_id) {
+ return false;
+ }
+
+ // In case of OpIAdd and OpISub, the type must be unsigned.
+ auto type = ir_context->get_type_mgr()->GetType(instruction.type_id());
+
+ switch (instruction_opcode) {
+ case SpvOpIAdd:
+ case SpvOpISub: {
+ // In case of OpIAdd and OpISub if the operand is a vector, the component
+ // type must be unsigned. Otherwise (if the operand is an int), the
+ // operand must be unsigned.
+ bool operand_is_signed =
+ type->AsVector()
+ ? type->AsVector()->element_type()->AsInteger()->IsSigned()
+ : type->AsInteger()->IsSigned();
+ if (operand_is_signed) {
+ return false;
+ }
+ } break;
+ default:
+ break;
+ }
+ return true;
+}
+
+protobufs::Transformation
+TransformationReplaceAddSubMulWithCarryingExtended::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_replace_add_sub_mul_with_carrying_extended() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
new file mode 100644
index 0000000..db71779
--- /dev/null
+++ b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2020 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.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceAddSubMulWithCarryingExtended
+ : public Transformation {
+ public:
+ explicit TransformationReplaceAddSubMulWithCarryingExtended(
+ const protobufs::TransformationReplaceAddSubMulWithCarryingExtended&
+ message);
+
+ explicit TransformationReplaceAddSubMulWithCarryingExtended(
+ uint32_t struct_fresh_id, uint32_t result_id);
+
+ // - |message_.struct_fresh_id| must be fresh.
+ // - |message_.result_id| must refer to an OpIAdd or OpISub or OpIMul
+ // instruction. In this instruction the result type id and the type ids of
+ // the operands must be the same.
+ // - The type of struct holding the intermediate result must exists in the
+ // module.
+ // - For OpIAdd, OpISub both operands must be unsigned.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // A transformation that replaces instructions OpIAdd, OpISub, OpIMul with
+ // pairs of instructions. The first one (OpIAddCarry, OpISubBorrow,
+ // OpUMulExtended, OpSMulExtended) computes the result into a struct. The
+ // second one extracts the appropriate component from the struct to yield the
+ // original result.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Checks if an OpIAdd, OpISub or OpIMul instruction can be used by the
+ // transformation.
+ bool static IsInstructionSuitable(opt::IRContext* ir_context,
+ const opt::Instruction& instruction);
+
+ private:
+ protobufs::TransformationReplaceAddSubMulWithCarryingExtended message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 6b3f72b..b4388d7 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -70,7 +70,7 @@
transformation_permute_function_parameters_test.cpp
transformation_permute_phi_operands_test.cpp
transformation_push_id_through_variable_test.cpp
- transformation_replace_parameter_with_global_test.cpp
+ transformation_replace_add_sub_mul_with_carrying_extended_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_copy_object_with_store_load_test.cpp
transformation_replace_constant_with_uniform_test.cpp
@@ -78,6 +78,7 @@
transformation_replace_id_with_synonym_test.cpp
transformation_replace_linear_algebra_instruction_test.cpp
transformation_replace_load_store_with_copy_memory_test.cpp
+ transformation_replace_parameter_with_global_test.cpp
transformation_replace_params_with_struct_test.cpp
transformation_set_function_control_test.cpp
transformation_set_loop_control_test.cpp
diff --git a/test/fuzz/transformation_replace_add_sub_mul_with_carrying_extended_test.cpp b/test/fuzz/transformation_replace_add_sub_mul_with_carrying_extended_test.cpp
new file mode 100644
index 0000000..258dc7f
--- /dev/null
+++ b/test/fuzz/transformation_replace_add_sub_mul_with_carrying_extended_test.cpp
@@ -0,0 +1,610 @@
+// Copyright (c) 2020 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 "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
+ NotApplicableBasicChecks) {
+ // First conditions in IsApplicable() are checked.
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "i1"
+ OpName %10 "i2"
+ OpName %12 "i3"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %13 = OpLoad %6 %10
+ %14 = OpLoad %6 %8
+ %15 = OpSDiv %6 %13 %14
+ OpStore %12 %15
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Bad: |struct_fresh_id| must be fresh.
+ auto transformation_bad_1 =
+ TransformationReplaceAddSubMulWithCarryingExtended(14, 15);
+ ASSERT_FALSE(
+ transformation_bad_1.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The transformation cannot be applied to an instruction OpSDiv.
+ auto transformation_bad_2 =
+ TransformationReplaceAddSubMulWithCarryingExtended(20, 15);
+ ASSERT_FALSE(
+ transformation_bad_2.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The transformation cannot be applied to an nonexistent instruction.
+ auto transformation_bad_3 =
+ TransformationReplaceAddSubMulWithCarryingExtended(20, 21);
+ ASSERT_FALSE(
+ transformation_bad_3.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
+ NotApplicableDifferingSignedTypes) {
+ // Operand types and result types do not match. Not applicable to an operation
+ // on vectors with signed integers and operation on signed integers.
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "i1"
+ OpName %10 "i2"
+ OpName %16 "v1"
+ OpName %20 "v2"
+ OpName %25 "v3"
+ OpName %31 "u1"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 1
+ %14 = OpTypeVector %6 3
+ %15 = OpTypePointer Function %14
+ %17 = OpConstant %6 0
+ %18 = OpConstant %6 2
+ %19 = OpConstantComposite %14 %17 %9 %18
+ %21 = OpConstant %6 3
+ %22 = OpConstant %6 4
+ %23 = OpConstant %6 5
+ %24 = OpConstantComposite %14 %21 %22 %23
+ %29 = OpTypeInt 32 0
+ %30 = OpTypePointer Function %29
+ %32 = OpConstant %29 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %16 = OpVariable %15 Function
+ %20 = OpVariable %15 Function
+ %25 = OpVariable %15 Function
+ %31 = OpVariable %30 Function
+ OpStore %8 %9
+ %11 = OpLoad %6 %8
+ %12 = OpLoad %6 %8
+ %13 = OpISub %6 %11 %12
+ OpStore %10 %13
+ OpStore %16 %19
+ OpStore %20 %24
+ %26 = OpLoad %14 %16
+ %27 = OpLoad %14 %20
+ %28 = OpIAdd %14 %26 %27
+ OpStore %25 %28
+ OpStore %31 %32
+ %40 = OpIMul %6 %32 %18
+ %41 = OpIAdd %6 %32 %32
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Bad: The transformation cannot be applied to an instruction OpIMul that has
+ // different signedness of the types of operands.
+ auto transformation_bad_1 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 40);
+ ASSERT_FALSE(
+ transformation_bad_1.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The transformation cannot be applied to an instruction OpIAdd that has
+ // different signedness of the result type than the signedness of the types of
+ // the operands.
+ auto transformation_bad_2 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 41);
+ ASSERT_FALSE(
+ transformation_bad_2.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The transformation cannot be applied to the instruction OpIAdd of two
+ // vectors that have signed components.
+ auto transformation_bad_3 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 28);
+ ASSERT_FALSE(
+ transformation_bad_3.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The transformation cannot be applied to the instruction OpISub of two
+ // signed integers
+ auto transformation_bad_4 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 13);
+ ASSERT_FALSE(
+ transformation_bad_4.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
+ NotApplicableMissingStructTypes) {
+ // In all cases the required struct types are missing.
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "u1"
+ OpName %10 "u2"
+ OpName %12 "u3"
+ OpName %24 "i1"
+ OpName %26 "i2"
+ OpName %28 "i3"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 1
+ %11 = OpConstant %6 2
+ %22 = OpTypeInt 32 1
+ %23 = OpTypePointer Function %22
+ %25 = OpConstant %22 1
+ %27 = OpConstant %22 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ %24 = OpVariable %23 Function
+ %26 = OpVariable %23 Function
+ %28 = OpVariable %23 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %13 = OpLoad %6 %8
+ %14 = OpLoad %6 %10
+ %15 = OpIAdd %6 %13 %14
+ OpStore %12 %15
+ %16 = OpLoad %6 %8
+ %17 = OpLoad %6 %10
+ %18 = OpISub %6 %16 %17
+ OpStore %12 %18
+ %19 = OpLoad %6 %8
+ %20 = OpLoad %6 %10
+ %21 = OpIMul %6 %19 %20
+ OpStore %12 %21
+ OpStore %24 %25
+ OpStore %26 %27
+ %29 = OpLoad %22 %24
+ %30 = OpLoad %22 %26
+ %31 = OpIMul %22 %29 %30
+ OpStore %28 %31
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto transformation_bad_1 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 15);
+ ASSERT_FALSE(
+ transformation_bad_1.IsApplicable(context.get(), transformation_context));
+
+ auto transformation_bad_2 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 18);
+ ASSERT_FALSE(
+ transformation_bad_2.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The transformation cannot be applied to the instruction OpIAdd of two
+ // vectors that have signed components.
+ auto transformation_bad_3 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 21);
+ ASSERT_FALSE(
+ transformation_bad_3.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The transformation cannot be applied to the instruction OpISub of two
+ // signed integers
+ auto transformation_bad_4 =
+ TransformationReplaceAddSubMulWithCarryingExtended(50, 31);
+ ASSERT_FALSE(
+ transformation_bad_4.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
+ ApplicableScenarios) {
+ // In this test all of the transformations can be applied. The required struct
+ // types are provided.
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "u1"
+ OpName %10 "u2"
+ OpName %12 "u3"
+ OpName %24 "i1"
+ OpName %26 "i2"
+ OpName %28 "i3"
+ OpName %34 "uv1"
+ OpName %36 "uv2"
+ OpName %39 "uv3"
+ OpName %51 "v1"
+ OpName %53 "v2"
+ OpName %56 "v3"
+ OpName %60 "pair_uint"
+ OpMemberName %60 0 "u_1"
+ OpMemberName %60 1 "u_2"
+ OpName %62 "p_uint"
+ OpName %63 "pair_uvec2"
+ OpMemberName %63 0 "uv_1"
+ OpMemberName %63 1 "uv_2"
+ OpName %65 "p_uvec2"
+ OpName %66 "pair_ivec2"
+ OpMemberName %66 0 "v_1"
+ OpMemberName %66 1 "v_2"
+ OpName %68 "p_ivec2"
+ OpName %69 "pair_int"
+ OpMemberName %69 0 "i_1"
+ OpMemberName %69 1 "i_2"
+ OpName %71 "p_int"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 1
+ %11 = OpConstant %6 2
+ %22 = OpTypeInt 32 1
+ %23 = OpTypePointer Function %22
+ %25 = OpConstant %22 1
+ %27 = OpConstant %22 2
+ %32 = OpTypeVector %6 2
+ %33 = OpTypePointer Function %32
+ %35 = OpConstantComposite %32 %9 %11
+ %37 = OpConstant %6 3
+ %38 = OpConstantComposite %32 %11 %37
+ %49 = OpTypeVector %22 2
+ %50 = OpTypePointer Function %49
+ %52 = OpConstantComposite %49 %25 %27
+ %54 = OpConstant %22 3
+ %55 = OpConstantComposite %49 %27 %54
+ %60 = OpTypeStruct %6 %6
+ %61 = OpTypePointer Private %60
+ %62 = OpVariable %61 Private
+ %63 = OpTypeStruct %32 %32
+ %64 = OpTypePointer Private %63
+ %65 = OpVariable %64 Private
+ %66 = OpTypeStruct %49 %49
+ %67 = OpTypePointer Private %66
+ %68 = OpVariable %67 Private
+ %69 = OpTypeStruct %22 %22
+ %70 = OpTypePointer Private %69
+ %71 = OpVariable %70 Private
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ %24 = OpVariable %23 Function
+ %26 = OpVariable %23 Function
+ %28 = OpVariable %23 Function
+ %34 = OpVariable %33 Function
+ %36 = OpVariable %33 Function
+ %39 = OpVariable %33 Function
+ %51 = OpVariable %50 Function
+ %53 = OpVariable %50 Function
+ %56 = OpVariable %50 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %13 = OpLoad %6 %8
+ %14 = OpLoad %6 %10
+ %15 = OpIAdd %6 %13 %14
+ OpStore %12 %15
+ %16 = OpLoad %6 %8
+ %17 = OpLoad %6 %10
+ %18 = OpISub %6 %16 %17
+ OpStore %12 %18
+ %19 = OpLoad %6 %8
+ %20 = OpLoad %6 %10
+ %21 = OpIMul %6 %19 %20
+ OpStore %12 %21
+ OpStore %24 %25
+ OpStore %26 %27
+ %29 = OpLoad %22 %24
+ %30 = OpLoad %22 %26
+ %31 = OpIMul %22 %29 %30
+ OpStore %28 %31
+ OpStore %34 %35
+ OpStore %36 %38
+ %40 = OpLoad %32 %34
+ %41 = OpLoad %32 %36
+ %42 = OpIAdd %32 %40 %41
+ OpStore %39 %42
+ %43 = OpLoad %32 %34
+ %44 = OpLoad %32 %36
+ %45 = OpISub %32 %43 %44
+ OpStore %39 %45
+ %46 = OpLoad %32 %34
+ %47 = OpLoad %32 %36
+ %48 = OpIMul %32 %46 %47
+ OpStore %39 %48
+ OpStore %51 %52
+ OpStore %53 %55
+ %57 = OpLoad %49 %51
+ %58 = OpLoad %49 %53
+ %59 = OpIMul %49 %57 %58
+ OpStore %56 %59
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto transformation_good_1 =
+ TransformationReplaceAddSubMulWithCarryingExtended(80, 15);
+ ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_1.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_2 =
+ TransformationReplaceAddSubMulWithCarryingExtended(81, 18);
+ ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_2.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_3 =
+ TransformationReplaceAddSubMulWithCarryingExtended(82, 21);
+ ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_3.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_4 =
+ TransformationReplaceAddSubMulWithCarryingExtended(83, 31);
+ ASSERT_TRUE(transformation_good_4.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_4.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_5 =
+ TransformationReplaceAddSubMulWithCarryingExtended(84, 42);
+ ASSERT_TRUE(transformation_good_5.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_5.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_6 =
+ TransformationReplaceAddSubMulWithCarryingExtended(85, 45);
+ ASSERT_TRUE(transformation_good_6.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_6.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_7 =
+ TransformationReplaceAddSubMulWithCarryingExtended(86, 48);
+ ASSERT_TRUE(transformation_good_7.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_7.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_8 =
+ TransformationReplaceAddSubMulWithCarryingExtended(87, 59);
+ ASSERT_TRUE(transformation_good_8.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_8.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "u1"
+ OpName %10 "u2"
+ OpName %12 "u3"
+ OpName %24 "i1"
+ OpName %26 "i2"
+ OpName %28 "i3"
+ OpName %34 "uv1"
+ OpName %36 "uv2"
+ OpName %39 "uv3"
+ OpName %51 "v1"
+ OpName %53 "v2"
+ OpName %56 "v3"
+ OpName %60 "pair_uint"
+ OpMemberName %60 0 "u_1"
+ OpMemberName %60 1 "u_2"
+ OpName %62 "p_uint"
+ OpName %63 "pair_uvec2"
+ OpMemberName %63 0 "uv_1"
+ OpMemberName %63 1 "uv_2"
+ OpName %65 "p_uvec2"
+ OpName %66 "pair_ivec2"
+ OpMemberName %66 0 "v_1"
+ OpMemberName %66 1 "v_2"
+ OpName %68 "p_ivec2"
+ OpName %69 "pair_int"
+ OpMemberName %69 0 "i_1"
+ OpMemberName %69 1 "i_2"
+ OpName %71 "p_int"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 1
+ %11 = OpConstant %6 2
+ %22 = OpTypeInt 32 1
+ %23 = OpTypePointer Function %22
+ %25 = OpConstant %22 1
+ %27 = OpConstant %22 2
+ %32 = OpTypeVector %6 2
+ %33 = OpTypePointer Function %32
+ %35 = OpConstantComposite %32 %9 %11
+ %37 = OpConstant %6 3
+ %38 = OpConstantComposite %32 %11 %37
+ %49 = OpTypeVector %22 2
+ %50 = OpTypePointer Function %49
+ %52 = OpConstantComposite %49 %25 %27
+ %54 = OpConstant %22 3
+ %55 = OpConstantComposite %49 %27 %54
+ %60 = OpTypeStruct %6 %6
+ %61 = OpTypePointer Private %60
+ %62 = OpVariable %61 Private
+ %63 = OpTypeStruct %32 %32
+ %64 = OpTypePointer Private %63
+ %65 = OpVariable %64 Private
+ %66 = OpTypeStruct %49 %49
+ %67 = OpTypePointer Private %66
+ %68 = OpVariable %67 Private
+ %69 = OpTypeStruct %22 %22
+ %70 = OpTypePointer Private %69
+ %71 = OpVariable %70 Private
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ %24 = OpVariable %23 Function
+ %26 = OpVariable %23 Function
+ %28 = OpVariable %23 Function
+ %34 = OpVariable %33 Function
+ %36 = OpVariable %33 Function
+ %39 = OpVariable %33 Function
+ %51 = OpVariable %50 Function
+ %53 = OpVariable %50 Function
+ %56 = OpVariable %50 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %13 = OpLoad %6 %8
+ %14 = OpLoad %6 %10
+ %80 = OpIAddCarry %60 %13 %14
+ %15 = OpCompositeExtract %6 %80 0
+ OpStore %12 %15
+ %16 = OpLoad %6 %8
+ %17 = OpLoad %6 %10
+ %81 = OpISubBorrow %60 %16 %17
+ %18 = OpCompositeExtract %6 %81 0
+ OpStore %12 %18
+ %19 = OpLoad %6 %8
+ %20 = OpLoad %6 %10
+ %82 = OpUMulExtended %60 %19 %20
+ %21 = OpCompositeExtract %6 %82 0
+ OpStore %12 %21
+ OpStore %24 %25
+ OpStore %26 %27
+ %29 = OpLoad %22 %24
+ %30 = OpLoad %22 %26
+ %83 = OpSMulExtended %69 %29 %30
+ %31 = OpCompositeExtract %22 %83 0
+ OpStore %28 %31
+ OpStore %34 %35
+ OpStore %36 %38
+ %40 = OpLoad %32 %34
+ %41 = OpLoad %32 %36
+ %84 = OpIAddCarry %63 %40 %41
+ %42 = OpCompositeExtract %32 %84 0
+ OpStore %39 %42
+ %43 = OpLoad %32 %34
+ %44 = OpLoad %32 %36
+ %85 = OpISubBorrow %63 %43 %44
+ %45 = OpCompositeExtract %32 %85 0
+ OpStore %39 %45
+ %46 = OpLoad %32 %34
+ %47 = OpLoad %32 %36
+ %86 = OpUMulExtended %63 %46 %47
+ %48 = OpCompositeExtract %32 %86 0
+ OpStore %39 %48
+ OpStore %51 %52
+ OpStore %53 %55
+ %57 = OpLoad %49 %51
+ %58 = OpLoad %49 %53
+ %87 = OpSMulExtended %66 %57 %58
+ %59 = OpCompositeExtract %49 %87 0
+ OpStore %56 %59
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools