mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 16:51:06 +00:00
spirv-fuzz: Fuzzer pass to interchange zero-like constants (#3524)
This fuzzer pass: For each zero-like constant, either finds the existing definition of the corresponding toggled one (OpConstantNull becomes zero-valued scalar OpConstant or vice versa) or creates a new one if it doesn't exist and records that the two are synonyms For each use of these constants, probabilistically decides whether to change it with the corresponding toggled constant id (as described in #3486 ) Only uses inside blocks of instructions are considered and not, for example, in instructions declaring other constants.
This commit is contained in:
parent
3e7238c68d
commit
f12c40f5a6
@ -64,6 +64,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_copy_objects.h
|
||||
fuzzer_pass_donate_modules.h
|
||||
fuzzer_pass_invert_comparison_operators.h
|
||||
fuzzer_pass_interchange_zero_like_constants.h
|
||||
fuzzer_pass_merge_blocks.h
|
||||
fuzzer_pass_obfuscate_constants.h
|
||||
fuzzer_pass_outline_functions.h
|
||||
@ -183,6 +184,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_copy_objects.cpp
|
||||
fuzzer_pass_donate_modules.cpp
|
||||
fuzzer_pass_invert_comparison_operators.cpp
|
||||
fuzzer_pass_interchange_zero_like_constants.cpp
|
||||
fuzzer_pass_merge_blocks.cpp
|
||||
fuzzer_pass_obfuscate_constants.cpp
|
||||
fuzzer_pass_outline_functions.cpp
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "fuzzer_pass_adjust_memory_operands_masks.h"
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
|
||||
@ -41,11 +40,13 @@
|
||||
#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
|
||||
#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_zero_like_constants.h"
|
||||
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
|
||||
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
|
||||
@ -330,6 +331,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
|
||||
&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);
|
||||
MaybeAddPass<FuzzerPassPermutePhiOperands>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
|
@ -64,6 +64,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
|
||||
{50, 95};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
|
||||
10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
|
||||
20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
|
||||
@ -181,6 +183,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
|
||||
chance_of_going_deeper_when_making_access_chain_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
|
||||
chance_of_interchanging_zero_like_constants_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
|
||||
chance_of_inverting_comparison_operators_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
|
||||
chance_of_making_donor_livesafe_ =
|
||||
|
@ -183,6 +183,9 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
|
||||
return chance_of_going_deeper_when_making_access_chain_;
|
||||
}
|
||||
uint32_t GetChanceOfInterchangingZeroLikeConstants() {
|
||||
return chance_of_interchanging_zero_like_constants_;
|
||||
}
|
||||
uint32_t GetChanceOfInvertingComparisonOperators() {
|
||||
return chance_of_inverting_comparison_operators_;
|
||||
}
|
||||
@ -325,6 +328,7 @@ class FuzzerContext {
|
||||
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_zero_like_constants_;
|
||||
uint32_t chance_of_inverting_comparison_operators_;
|
||||
uint32_t chance_of_making_donor_livesafe_;
|
||||
uint32_t chance_of_merging_blocks_;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_constant_boolean.h"
|
||||
#include "source/fuzz/transformation_add_constant_composite.h"
|
||||
#include "source/fuzz/transformation_add_constant_null.h"
|
||||
#include "source/fuzz/transformation_add_constant_scalar.h"
|
||||
#include "source/fuzz/transformation_add_global_undef.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
@ -373,6 +374,27 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) {
|
||||
// Find existing declaration
|
||||
opt::analysis::NullConstant null_constant(
|
||||
GetIRContext()->get_type_mgr()->GetType(type_id));
|
||||
auto existing_constant =
|
||||
GetIRContext()->get_constant_mgr()->FindConstant(&null_constant);
|
||||
|
||||
// Return if found
|
||||
if (existing_constant) {
|
||||
return GetIRContext()
|
||||
->get_constant_mgr()
|
||||
->GetDefiningInstruction(existing_constant)
|
||||
->result_id();
|
||||
}
|
||||
|
||||
// Create new if not found
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantNull(result, type_id));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
||||
FuzzerPass::GetAvailableBasicTypesAndPointers(
|
||||
SpvStorageClass storage_class) const {
|
||||
|
@ -192,6 +192,12 @@ class FuzzerPass {
|
||||
// If no such instruction exists, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
|
||||
|
||||
// Returns the id of an OpNullConstant instruction of type |type_id|. If
|
||||
// that instruction doesn't exist, it is added through a transformation.
|
||||
// |type_id| must be a valid result id of an OpType* instruction that exists
|
||||
// in the module.
|
||||
uint32_t FindOrCreateNullConstant(uint32_t type_id);
|
||||
|
||||
// Define a *basic type* to be an integer, boolean or floating-point type,
|
||||
// or a matrix, vector, struct or fixed-size array built from basic types. In
|
||||
// particular, a basic type cannot contain an opaque type (such as an image),
|
||||
|
@ -73,10 +73,9 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
continue;
|
||||
}
|
||||
// |use_index| is the absolute index of the operand. We require
|
||||
// the index of the operand restricted to input operands only, so
|
||||
// we subtract the number of non-input operands from |use_index|.
|
||||
// the index of the operand restricted to input operands only.
|
||||
uint32_t use_in_operand_index =
|
||||
use_index - use_inst->NumOperands() + use_inst->NumInOperands();
|
||||
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
|
||||
if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
|
||||
GetIRContext(), use_inst, use_in_operand_index)) {
|
||||
continue;
|
||||
|
124
source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
Normal file
124
source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2020 Stefano Milizia
|
||||
// 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_interchange_zero_like_constants.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 {
|
||||
FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassInterchangeZeroLikeConstants::
|
||||
~FuzzerPassInterchangeZeroLikeConstants() = default;
|
||||
|
||||
uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
|
||||
opt::Instruction* declaration) {
|
||||
auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
|
||||
declaration->result_id());
|
||||
|
||||
// This pass only toggles zero-like constants
|
||||
if (!constant->IsZero()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (constant->AsScalarConstant()) {
|
||||
return FindOrCreateNullConstant(declaration->type_id());
|
||||
} else if (constant->AsNullConstant()) {
|
||||
// Add declaration of equivalent scalar constant
|
||||
auto kind = constant->type()->kind();
|
||||
if (kind == opt::analysis::Type::kBool ||
|
||||
kind == opt::analysis::Type::kInteger ||
|
||||
kind == opt::analysis::Type::kFloat) {
|
||||
return FindOrCreateZeroConstant(declaration->type_id());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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();
|
||||
uint32_t toggled_id = FindOrCreateToggledConstant(constant);
|
||||
|
||||
if (!toggled_id) {
|
||||
// Not a zero-like constant
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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()
|
||||
->GetChanceOfInterchangingZeroLikeConstants())) {
|
||||
MaybeAddUseToReplace(use_inst, use_index, toggled_id,
|
||||
&uses_to_replace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the ids
|
||||
for (auto use_to_replace : uses_to_replace) {
|
||||
auto transformation = TransformationReplaceIdWithSynonym(
|
||||
use_to_replace.first, use_to_replace.second);
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
63
source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
Normal file
63
source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2020 Stefano Milizia
|
||||
// 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_ZERO_LIKE_CONSTANTS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that:
|
||||
// - Finds all the zero-like constant definitions in the module and adds the
|
||||
// definitions of the corresponding synonym, recording the fact that they
|
||||
// are synonymous. If the synonym is already in the module, it does not
|
||||
// add a new one.
|
||||
// - For each use of a zero-like constant, decides whether to change it to the
|
||||
// id of the toggled constant.
|
||||
class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassInterchangeZeroLikeConstants(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassInterchangeZeroLikeConstants() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Given the declaration of a zero-like constant, it finds or creates the
|
||||
// corresponding toggled constant (a scalar constant of value 0 becomes a
|
||||
// null constant of the same type and vice versa).
|
||||
// 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
|
||||
} // namespace spvtools
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
@ -552,6 +552,12 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
|
||||
uint32_t absolute_index) {
|
||||
// Subtract the number of non-input operands from the index
|
||||
return absolute_index - inst.NumOperands() + inst.NumInOperands();
|
||||
}
|
||||
|
||||
bool IsNullConstantSupported(const opt::analysis::Type& type) {
|
||||
return type.AsBool() || type.AsInteger() || type.AsFloat() ||
|
||||
type.AsMatrix() || type.AsVector() || type.AsArray() ||
|
||||
|
@ -213,6 +213,11 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
|
||||
uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
|
||||
SpvStorageClass storage_class);
|
||||
|
||||
// Given an instruction |inst| and an operand absolute index |absolute_index|,
|
||||
// returns the index of the operand restricted to the input operands.
|
||||
uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
|
||||
uint32_t absolute_index);
|
||||
|
||||
// Returns true if and only if |type| is one of the types for which it is legal
|
||||
// to have an OpConstantNull value.
|
||||
bool IsNullConstantSupported(const opt::analysis::Type& type);
|
||||
|
Loading…
Reference in New Issue
Block a user