spirv-fuzz: Pass to replace int operands with ints of opposite signedness (#3612)
This PR introduces a new fuzzer pass, which:
- finds all integer vectors or constants
- finds or creates the corresponding constants with opposite
signedness
- records such constants as synonyms of the first ones
- replaces the usages of the original constants with the new ones
if allowed
Fixes #2677.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 16075c8..e8c9fcb 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -65,6 +65,7 @@
fuzzer_pass_copy_objects.h
fuzzer_pass_donate_modules.h
fuzzer_pass_invert_comparison_operators.h
+ fuzzer_pass_interchange_signedness_of_integer_operands.h
fuzzer_pass_interchange_zero_like_constants.h
fuzzer_pass_merge_blocks.h
fuzzer_pass_obfuscate_constants.h
@@ -194,6 +195,7 @@
fuzzer_pass_copy_objects.cpp
fuzzer_pass_donate_modules.cpp
fuzzer_pass_invert_comparison_operators.cpp
+ fuzzer_pass_interchange_signedness_of_integer_operands.cpp
fuzzer_pass_interchange_zero_like_constants.cpp
fuzzer_pass_merge_blocks.cpp
fuzzer_pass_obfuscate_constants.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index f549590..ae4332b 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -46,6 +46,7 @@
#include "source/fuzz/fuzzer_pass_construct_composites.h"
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_donate_modules.h"
+#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
@@ -335,6 +336,9 @@
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
+ &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 3f1fe16..dacdc58 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -67,6 +67,8 @@
{50, 95};
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
10, 90};
+const std::pair<uint32_t, uint32_t>
+ kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
@@ -197,6 +199,8 @@
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_going_deeper_when_making_access_chain_ =
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
+ chance_of_interchanging_signedness_of_integer_operands_ =
+ ChooseBetweenMinAndMax(kChanceOfInterchangingSignednessOfIntegerOperands);
chance_of_interchanging_zero_like_constants_ =
ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
chance_of_inverting_comparison_operators_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 3376c1e..24aad0c 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -186,6 +186,9 @@
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
return chance_of_going_deeper_when_making_access_chain_;
}
+ uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() {
+ return chance_of_interchanging_signedness_of_integer_operands_;
+ }
uint32_t GetChanceOfInterchangingZeroLikeConstants() {
return chance_of_interchanging_zero_like_constants_;
}
@@ -353,6 +356,7 @@
uint32_t chance_of_copying_object_;
uint32_t chance_of_donating_additional_module_;
uint32_t chance_of_going_deeper_when_making_access_chain_;
+ uint32_t chance_of_interchanging_signedness_of_integer_operands_;
uint32_t chance_of_interchanging_zero_like_constants_;
uint32_t chance_of_inverting_comparison_operators_;
uint32_t chance_of_making_donor_livesafe_;
diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
index ebd88d0..8d1b28c 100644
--- a/source/fuzz/fuzzer_pass.cpp
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -17,6 +17,7 @@
#include <set>
#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_composite.h"
@@ -514,5 +515,23 @@
}
}
+void FuzzerPass::MaybeAddUseToReplace(
+ opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+ std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+ uses_to_replace) {
+ // Only consider this use if it is in a block
+ if (!GetIRContext()->get_instr_block(use_inst)) {
+ return;
+ }
+
+ // Get the index of the operand restricted to input operands.
+ uint32_t in_operand_index =
+ fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
+ auto id_use_descriptor =
+ MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
+ uses_to_replace->emplace_back(
+ std::make_pair(id_use_descriptor, replacement_id));
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h
index 4731b74..2777f24 100644
--- a/source/fuzz/fuzzer_pass.h
+++ b/source/fuzz/fuzzer_pass.h
@@ -273,6 +273,16 @@
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
bool is_irrelevant);
+ // Adds a pair (id_use_descriptor, |replacement_id|) to the vector
+ // |uses_to_replace|, where id_use_descriptor is the id use descriptor
+ // representing the usage of an id in the |use_inst| instruction, at operand
+ // index |use_index|, only if the instruction is in a basic block.
+ // If the instruction is not in a basic block, it does nothing.
+ void MaybeAddUseToReplace(
+ opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+ std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+ uses_to_replace);
+
private:
opt::IRContext* ir_context_;
TransformationContext* transformation_context_;
diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
new file mode 100644
index 0000000..6c3aa7b
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
@@ -0,0 +1,149 @@
+// 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 "fuzzer_pass_interchange_signedness_of_integer_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/transformation_record_synonymous_constants.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassInterchangeSignednessOfIntegerOperands::
+ FuzzerPassInterchangeSignednessOfIntegerOperands(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassInterchangeSignednessOfIntegerOperands::
+ ~FuzzerPassInterchangeSignednessOfIntegerOperands() = default;
+
+void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
+ // Make vector keeping track of all the uses we want to replace.
+ // This is a vector of pairs, where the first element is an id use descriptor
+ // identifying the use of a constant id and the second is the id that should
+ // be used to replace it.
+ std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
+
+ for (auto constant : GetIRContext()->GetConstants()) {
+ uint32_t constant_id = constant->result_id();
+
+ // We want to record the synonymity of an integer constant with another
+ // constant with opposite signedness, and this can only be done if they are
+ // not irrelevant.
+ if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+ constant_id)) {
+ continue;
+ }
+
+ uint32_t toggled_id =
+ FindOrCreateToggledIntegerConstant(constant->result_id());
+ if (!toggled_id) {
+ // Not an integer constant
+ continue;
+ }
+
+ assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+ toggled_id) &&
+ "FindOrCreateToggledConstant can't produce an irrelevant id");
+
+ // Record synonymous constants
+ ApplyTransformation(
+ TransformationRecordSynonymousConstants(constant_id, toggled_id));
+
+ // Find all the uses of the constant and, for each, probabilistically
+ // decide whether to replace it.
+ GetIRContext()->get_def_use_mgr()->ForEachUse(
+ constant_id,
+ [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
+ uint32_t use_index) -> void {
+ if (GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfInterchangingSignednessOfIntegerOperands())) {
+ MaybeAddUseToReplace(use_inst, use_index, toggled_id,
+ &uses_to_replace);
+ }
+ });
+ }
+
+ // Replace the ids if it is allowed.
+ for (auto use_to_replace : uses_to_replace) {
+ MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
+ use_to_replace.first, use_to_replace.second));
+ }
+}
+
+uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands::
+ FindOrCreateToggledIntegerConstant(uint32_t id) {
+ auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
+
+ // This pass only toggles integer constants.
+ if (!constant->AsIntConstant() &&
+ (!constant->AsVectorConstant() ||
+ !constant->AsVectorConstant()->component_type()->AsInteger())) {
+ return 0;
+ }
+
+ if (auto integer = constant->AsIntConstant()) {
+ auto type = integer->type()->AsInteger();
+
+ // Find or create and return the toggled constant.
+ return FindOrCreateIntegerConstant(std::vector<uint32_t>(integer->words()),
+ type->width(), !type->IsSigned(), false);
+ }
+
+ // The constant is an integer vector.
+
+ // Find the component type.
+ auto component_type =
+ constant->AsVectorConstant()->component_type()->AsInteger();
+
+ // Find or create the toggled component type.
+ uint32_t toggled_component_type = FindOrCreateIntegerType(
+ component_type->width(), !component_type->IsSigned());
+
+ // Get the information about the toggled components. We need to extract this
+ // information now because the analyses might be invalidated, which would make
+ // the constant and component_type variables invalid.
+ std::vector<std::vector<uint32_t>> component_words;
+
+ for (auto component : constant->AsVectorConstant()->GetComponents()) {
+ component_words.push_back(component->AsIntConstant()->words());
+ }
+ uint32_t width = component_type->width();
+ bool is_signed = !component_type->IsSigned();
+
+ std::vector<uint32_t> toggled_components;
+
+ // Find or create the toggled components.
+ for (auto words : component_words) {
+ toggled_components.push_back(
+ FindOrCreateIntegerConstant(words, width, is_signed, false));
+ }
+
+ // Find or create the required toggled vector type.
+ uint32_t toggled_type = FindOrCreateVectorType(
+ toggled_component_type, (uint32_t)toggled_components.size());
+
+ // Find or create and return the toggled vector constant.
+ return FindOrCreateCompositeConstant(toggled_components, toggled_type, false);
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
new file mode 100644
index 0000000..4824218
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
@@ -0,0 +1,51 @@
+// 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 SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
+#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that:
+// - Finds all the integer constant (scalar and vector) definitions in the
+// module and adds the definitions of the integer with the same data words but
+// opposite signedness. If the synonym is already in the module, it does not
+// add a new one.
+// - For each use of an integer constant where its signedness does not matter,
+// decides whether to change it to the id of the toggled constant.
+class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass {
+ public:
+ FuzzerPassInterchangeSignednessOfIntegerOperands(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassInterchangeSignednessOfIntegerOperands() override;
+
+ void Apply() override;
+
+ private:
+ // Given the id of an integer constant (scalar or vector), it finds or creates
+ // the corresponding toggled constant (the integer with the same data words
+ // but opposite signedness). Returns the id of the toggled instruction if the
+ // constant is an integer scalar or vector, 0 otherwise.
+ uint32_t FindOrCreateToggledIntegerConstant(uint32_t id);
+};
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
index 727132e..8bd670f 100644
--- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
+++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
@@ -57,24 +57,6 @@
return 0;
}
-void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
- opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
- std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
- uses_to_replace) {
- // Only consider this use if it is in a block
- if (!GetIRContext()->get_instr_block(use_inst)) {
- return;
- }
-
- // Get the index of the operand restricted to input operands.
- uint32_t in_operand_index =
- fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
- auto id_use_descriptor =
- MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
- uses_to_replace->emplace_back(
- std::make_pair(id_use_descriptor, replacement_id));
-}
-
void FuzzerPassInterchangeZeroLikeConstants::Apply() {
// Make vector keeping track of all the uses we want to replace.
// This is a vector of pairs, where the first element is an id use descriptor
@@ -118,7 +100,7 @@
});
}
- // Replace the ids
+ // Replace the ids if it is allowed.
for (auto use_to_replace : uses_to_replace) {
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
use_to_replace.first, use_to_replace.second));
diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
index 4fcc44e..4ea990a 100644
--- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
+++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
@@ -46,16 +46,6 @@
// Returns the id of the toggled instruction if the constant is zero-like,
// 0 otherwise.
uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
-
- // Given an id use (described by an instruction and an index) and an id with
- // which the original one should be replaced, adds a pair (with the elements
- // being the corresponding id use descriptor and the replacement id) to
- // |uses_to_replace| if the use is in an instruction block, otherwise does
- // nothing.
- void MaybeAddUseToReplace(
- opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
- std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
- uses_to_replace);
};
} // namespace fuzz