mirror of https://github.com/bkaradzic/bgfx
Updated spirv-tools.
This commit is contained in:
parent
9522e65f0f
commit
6a0b5b5b5c
|
@ -1 +1 @@
|
|||
"v2020.3-dev", "SPIRV-Tools v2020.3-dev eff6f130119f3e6acbf81f8432c0912296d4dfdb"
|
||||
"v2020.3-dev", "SPIRV-Tools v2020.3-dev 9522e65f0f430a45a29053e2aeec768c2dbca075"
|
||||
|
|
|
@ -79,6 +79,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||
transformation_access_chain.h
|
||||
transformation_add_constant_boolean.h
|
||||
transformation_add_constant_composite.h
|
||||
transformation_add_constant_null.h
|
||||
transformation_add_constant_scalar.h
|
||||
transformation_add_dead_block.h
|
||||
transformation_add_dead_break.h
|
||||
|
@ -99,6 +100,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||
transformation_add_type_vector.h
|
||||
transformation_composite_construct.h
|
||||
transformation_composite_extract.h
|
||||
transformation_context.h
|
||||
transformation_copy_object.h
|
||||
transformation_equation_instruction.h
|
||||
transformation_function_call.h
|
||||
|
@ -170,6 +172,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||
transformation_access_chain.cpp
|
||||
transformation_add_constant_boolean.cpp
|
||||
transformation_add_constant_composite.cpp
|
||||
transformation_add_constant_null.cpp
|
||||
transformation_add_constant_scalar.cpp
|
||||
transformation_add_dead_block.cpp
|
||||
transformation_add_dead_break.cpp
|
||||
|
@ -190,6 +193,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||
transformation_add_type_vector.cpp
|
||||
transformation_composite_construct.cpp
|
||||
transformation_composite_extract.cpp
|
||||
transformation_context.cpp
|
||||
transformation_copy_object.cpp
|
||||
transformation_equation_instruction.cpp
|
||||
transformation_function_call.cpp
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
#include "source/opt/build_module.h"
|
||||
|
@ -159,7 +160,8 @@ MakeConstantUniformReplacement(opt::IRContext* ir_context,
|
|||
} // namespace
|
||||
|
||||
bool ForceRenderRed(
|
||||
const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
|
||||
const spv_target_env& target_env, spv_validator_options validator_options,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
std::vector<uint32_t>* binary_out) {
|
||||
auto message_consumer = spvtools::utils::CLIMessageConsumer;
|
||||
|
@ -171,7 +173,7 @@ bool ForceRenderRed(
|
|||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size())) {
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options)) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return false;
|
||||
|
@ -187,6 +189,8 @@ bool ForceRenderRed(
|
|||
for (auto& fact : initial_facts.fact()) {
|
||||
fact_manager.AddFact(fact, ir_context.get());
|
||||
}
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
validator_options);
|
||||
|
||||
auto entry_point_function =
|
||||
FindFragmentShaderEntryPoint(ir_context.get(), message_consumer);
|
||||
|
@ -355,8 +359,9 @@ bool ForceRenderRed(
|
|||
for (auto& replacement : {first_greater_then_operand_replacement.get(),
|
||||
second_greater_then_operand_replacement.get()}) {
|
||||
if (replacement) {
|
||||
assert(replacement->IsApplicable(ir_context.get(), fact_manager));
|
||||
replacement->Apply(ir_context.get(), &fact_manager);
|
||||
assert(replacement->IsApplicable(ir_context.get(),
|
||||
transformation_context));
|
||||
replacement->Apply(ir_context.get(), &transformation_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@ namespace fuzz {
|
|||
// instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for
|
||||
// which it is known that 'u < v' holds.
|
||||
bool ForceRenderRed(
|
||||
const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
|
||||
const spv_target_env& target_env, spv_validator_options validator_options,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
std::vector<uint32_t>* binary_out);
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/pseudo_random_generator.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/spirv_fuzzer_options.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
@ -66,19 +67,19 @@ const uint32_t kTransformationLimit = 500;
|
|||
const uint32_t kChanceOfApplyingAnotherPass = 85;
|
||||
|
||||
// A convenience method to add a fuzzer pass to |passes| with probability 0.5.
|
||||
// All fuzzer passes take |ir_context|, |fact_manager|, |fuzzer_context| and
|
||||
// |transformation_sequence_out| as parameters. Extra arguments can be provided
|
||||
// via |extra_args|.
|
||||
// All fuzzer passes take |ir_context|, |transformation_context|,
|
||||
// |fuzzer_context| and |transformation_sequence_out| as parameters. Extra
|
||||
// arguments can be provided via |extra_args|.
|
||||
template <typename T, typename... Args>
|
||||
void MaybeAddPass(
|
||||
std::vector<std::unique_ptr<FuzzerPass>>* passes,
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformation_sequence_out,
|
||||
Args&&... extra_args) {
|
||||
if (fuzzer_context->ChooseEven()) {
|
||||
passes->push_back(MakeUnique<T>(ir_context, fact_manager, fuzzer_context,
|
||||
transformation_sequence_out,
|
||||
passes->push_back(MakeUnique<T>(ir_context, transformation_context,
|
||||
fuzzer_context, transformation_sequence_out,
|
||||
std::forward<Args>(extra_args)...));
|
||||
}
|
||||
}
|
||||
|
@ -86,26 +87,31 @@ void MaybeAddPass(
|
|||
} // namespace
|
||||
|
||||
struct Fuzzer::Impl {
|
||||
explicit Impl(spv_target_env env, uint32_t random_seed,
|
||||
bool validate_after_each_pass)
|
||||
Impl(spv_target_env env, uint32_t random_seed, bool validate_after_each_pass,
|
||||
spv_validator_options options)
|
||||
: target_env(env),
|
||||
seed(random_seed),
|
||||
validate_after_each_fuzzer_pass(validate_after_each_pass) {}
|
||||
validate_after_each_fuzzer_pass(validate_after_each_pass),
|
||||
validator_options(options) {}
|
||||
|
||||
bool ApplyPassAndCheckValidity(FuzzerPass* pass,
|
||||
const opt::IRContext& ir_context,
|
||||
const spvtools::SpirvTools& tools) const;
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
const uint32_t seed; // Seed for random number generator.
|
||||
bool validate_after_each_fuzzer_pass; // Determines whether the validator
|
||||
// should be invoked after every fuzzer pass.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
// should be invoked after every fuzzer
|
||||
// pass.
|
||||
spv_validator_options validator_options; // Options to control validation.
|
||||
};
|
||||
|
||||
Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass)
|
||||
: impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass)) {}
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options)
|
||||
: impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass,
|
||||
validator_options)) {}
|
||||
|
||||
Fuzzer::~Fuzzer() = default;
|
||||
|
||||
|
@ -120,7 +126,8 @@ bool Fuzzer::Impl::ApplyPassAndCheckValidity(
|
|||
if (validate_after_each_fuzzer_pass) {
|
||||
std::vector<uint32_t> binary_to_validate;
|
||||
ir_context.module()->ToBinary(&binary_to_validate, false);
|
||||
if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) {
|
||||
if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(),
|
||||
validator_options)) {
|
||||
consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during fuzzing (set a breakpoint to "
|
||||
"inspect); stopping.");
|
||||
|
@ -149,7 +156,8 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size())) {
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(),
|
||||
impl_->validator_options)) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid;
|
||||
|
@ -175,11 +183,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||
|
||||
FactManager fact_manager;
|
||||
fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
impl_->validator_options);
|
||||
|
||||
// Add some essential ingredients to the module if they are not already
|
||||
// present, such as boolean constants.
|
||||
FuzzerPassAddUsefulConstructs add_useful_constructs(
|
||||
ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
|
||||
tools)) {
|
||||
|
@ -189,69 +199,69 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||
// Apply some semantics-preserving passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> passes;
|
||||
while (passes.empty()) {
|
||||
MaybeAddPass<FuzzerPassAddAccessChains>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddCompositeTypes>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadBlocks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadBreaks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddAccessChains>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddCompositeTypes>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadBreaks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadContinues>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddEquationInstructions>(
|
||||
&passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddFunctionCalls>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddGlobalVariables>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(), &fact_manager,
|
||||
&fuzzer_context,
|
||||
MaybeAddPass<FuzzerPassAddFunctionCalls>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddGlobalVariables>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
|
||||
&transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddLocalVariables>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(), &fact_manager,
|
||||
&fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassConstructComposites>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassDonateModules>(
|
||||
&passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out, donor_suppliers);
|
||||
MaybeAddPass<FuzzerPassMergeBlocks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassOutlineFunctions>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
|
||||
&passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
MaybeAddPass<FuzzerPassAddLocalVariables>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
|
||||
&transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassApplyIdSynonyms>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassConstructComposites>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassCopyObjects>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassDonateModules>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out, donor_suppliers);
|
||||
MaybeAddPass<FuzzerPassMergeBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassObfuscateConstants>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassOutlineFunctions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermuteBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSplitBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
}
|
||||
|
||||
bool is_first = true;
|
||||
|
@ -272,25 +282,25 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||
// as they do not unlock other passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> final_passes;
|
||||
MaybeAddPass<FuzzerPassAdjustFunctionControls>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustLoopControls>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustLoopControls>(&final_passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustMemoryOperandsMasks>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
for (auto& pass : final_passes) {
|
||||
if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
|
||||
|
|
|
@ -41,8 +41,9 @@ class Fuzzer {
|
|||
// seed for pseudo-random number generation.
|
||||
// |validate_after_each_fuzzer_pass| controls whether the validator will be
|
||||
// invoked after every fuzzer pass is applied.
|
||||
explicit Fuzzer(spv_target_env env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass);
|
||||
Fuzzer(spv_target_env env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
Fuzzer(const Fuzzer&) = delete;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_constant_boolean.h"
|
||||
|
@ -31,11 +33,12 @@
|
|||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPass::FuzzerPass(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: ir_context_(ir_context),
|
||||
fact_manager_(fact_manager),
|
||||
transformation_context_(transformation_context),
|
||||
fuzzer_context_(fuzzer_context),
|
||||
transformations_(transformations) {}
|
||||
|
||||
|
@ -328,43 +331,72 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
|
|||
}
|
||||
|
||||
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
||||
FuzzerPass::GetAvailableBaseTypesAndPointers(
|
||||
FuzzerPass::GetAvailableBasicTypesAndPointers(
|
||||
SpvStorageClass storage_class) const {
|
||||
// Records all of the base types available in the module.
|
||||
std::vector<uint32_t> base_types;
|
||||
// Records all of the basic types available in the module.
|
||||
std::set<uint32_t> basic_types;
|
||||
|
||||
// For each base type, records all the associated pointer types that target
|
||||
// that base type and that have |storage_class| as their storage class.
|
||||
std::map<uint32_t, std::vector<uint32_t>> base_type_to_pointers;
|
||||
// For each basic type, records all the associated pointer types that target
|
||||
// the basic type and that have |storage_class| as their storage class.
|
||||
std::map<uint32_t, std::vector<uint32_t>> basic_type_to_pointers;
|
||||
|
||||
for (auto& inst : GetIRContext()->types_values()) {
|
||||
// For each basic type that we come across, record type, and the fact that
|
||||
// we cannot yet have seen any pointers that use the basic type as its
|
||||
// pointee type.
|
||||
//
|
||||
// For pointer types with basic pointee types, associate the pointer type
|
||||
// with the basic type.
|
||||
switch (inst.opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeBool:
|
||||
case SpvOpTypeFloat:
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeStruct:
|
||||
case SpvOpTypeVector:
|
||||
// These types are suitable as pointer base types. Record the type,
|
||||
// and the fact that we cannot yet have seen any pointers that use this
|
||||
// as its base type.
|
||||
base_types.push_back(inst.result_id());
|
||||
base_type_to_pointers.insert({inst.result_id(), {}});
|
||||
// These are all basic types.
|
||||
basic_types.insert(inst.result_id());
|
||||
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||
break;
|
||||
case SpvOpTypePointer:
|
||||
if (inst.GetSingleWordInOperand(0) == storage_class) {
|
||||
// The pointer has the desired storage class, so we are interested in
|
||||
// it. Associate it with its base type.
|
||||
base_type_to_pointers.at(inst.GetSingleWordInOperand(1))
|
||||
.push_back(inst.result_id());
|
||||
case SpvOpTypeArray:
|
||||
// An array type is basic if its base type is basic.
|
||||
if (basic_types.count(inst.GetSingleWordInOperand(0))) {
|
||||
basic_types.insert(inst.result_id());
|
||||
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||
}
|
||||
break;
|
||||
case SpvOpTypeStruct: {
|
||||
// A struct type is basic if all of its members are basic.
|
||||
bool all_members_are_basic_types = true;
|
||||
for (uint32_t i = 0; i < inst.NumInOperands(); i++) {
|
||||
if (!basic_types.count(inst.GetSingleWordInOperand(i))) {
|
||||
all_members_are_basic_types = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_members_are_basic_types) {
|
||||
basic_types.insert(inst.result_id());
|
||||
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpvOpTypePointer: {
|
||||
// We are interested in the pointer if its pointee type is basic and it
|
||||
// has the right storage class.
|
||||
auto pointee_type = inst.GetSingleWordInOperand(1);
|
||||
if (inst.GetSingleWordInOperand(0) == storage_class &&
|
||||
basic_types.count(pointee_type)) {
|
||||
// The pointer has the desired storage class, and its pointee type is
|
||||
// a basic type, so we are interested in it. Associate it with its
|
||||
// basic type.
|
||||
basic_type_to_pointers.at(pointee_type).push_back(inst.result_id());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {base_types, base_type_to_pointers};
|
||||
return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers};
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateZeroConstant(
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
|
@ -29,22 +29,25 @@ namespace fuzz {
|
|||
// Interface for applying a pass of transformations to a module.
|
||||
class FuzzerPass {
|
||||
public:
|
||||
FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPass(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
virtual ~FuzzerPass();
|
||||
|
||||
// Applies the pass to the module |ir_context_|, assuming and updating
|
||||
// facts from |fact_manager_|, and using |fuzzer_context_| to guide the
|
||||
// process. Appends to |transformations_| all transformations that were
|
||||
// applied during the pass.
|
||||
// information from |transformation_context_|, and using |fuzzer_context_| to
|
||||
// guide the process. Appends to |transformations_| all transformations that
|
||||
// were applied during the pass.
|
||||
virtual void Apply() = 0;
|
||||
|
||||
protected:
|
||||
opt::IRContext* GetIRContext() const { return ir_context_; }
|
||||
|
||||
FactManager* GetFactManager() const { return fact_manager_; }
|
||||
TransformationContext* GetTransformationContext() const {
|
||||
return transformation_context_;
|
||||
}
|
||||
|
||||
FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; }
|
||||
|
||||
|
@ -93,9 +96,10 @@ class FuzzerPass {
|
|||
// by construction, and adding it to the sequence of applied transformations.
|
||||
template <typename TransformationType>
|
||||
void ApplyTransformation(const TransformationType& transformation) {
|
||||
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
assert(transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Transformation should be applicable by construction.");
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
|
||||
|
@ -165,18 +169,21 @@ class FuzzerPass {
|
|||
// If no such instruction exists, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
|
||||
|
||||
// Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that:
|
||||
// - base_type_ids captures every scalar or composite type declared in the
|
||||
// module (i.e., all int, bool, float, vector, matrix, struct and array
|
||||
// types
|
||||
// - base_type_ids_to_pointers maps every such base type to the sequence
|
||||
// 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),
|
||||
// or a runtime-sized array.
|
||||
//
|
||||
// Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that:
|
||||
// - basic_type_ids captures every basic type declared in the module.
|
||||
// - basic_type_ids_to_pointers maps every such basic type to the sequence
|
||||
// of all pointer types that have storage class |storage_class| and the
|
||||
// given base type as their pointee type. The sequence may be empty for
|
||||
// some base types if no pointers to those types are defined for the given
|
||||
// given basic type as their pointee type. The sequence may be empty for
|
||||
// some basic types if no pointers to those types are defined for the given
|
||||
// storage class, and the sequence will have multiple elements if there are
|
||||
// repeated pointer declarations for the same base type and storage class.
|
||||
// repeated pointer declarations for the same basic type and storage class.
|
||||
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
||||
GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const;
|
||||
GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const;
|
||||
|
||||
// Given a type id, |scalar_or_composite_type_id|, which must correspond to
|
||||
// some scalar or composite type, returns the result id of an instruction
|
||||
|
@ -230,7 +237,7 @@ class FuzzerPass {
|
|||
const std::vector<uint32_t>& constant_ids);
|
||||
|
||||
opt::IRContext* ir_context_;
|
||||
FactManager* fact_manager_;
|
||||
TransformationContext* transformation_context_;
|
||||
FuzzerContext* fuzzer_context_;
|
||||
protobufs::TransformationSequence* transformations_;
|
||||
};
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddAccessChains::FuzzerPassAddAccessChains(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddAccessChains : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddAccessChains(opt::IRContext* ir_context,
|
||||
FactManager* fact_manager,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -22,10 +22,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddCompositeTypes : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddCompositeTypes(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default;
|
||||
|
||||
|
@ -53,8 +54,9 @@ void FuzzerPassAddDeadBlocks::Apply() {
|
|||
}
|
||||
// Apply all those transformations that are in fact applicable.
|
||||
for (auto& transformation : candidate_transformations) {
|
||||
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ namespace fuzz {
|
|||
// passes can then manipulate such blocks.
|
||||
class FuzzerPassAddDeadBlocks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassAddDeadBlocks(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default;
|
||||
|
||||
|
@ -79,8 +80,8 @@ void FuzzerPassAddDeadBreaks::Apply() {
|
|||
auto candidate_transformation = TransformationAddDeadBreak(
|
||||
block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(),
|
||||
std::move(phi_ids));
|
||||
if (candidate_transformation.IsApplicable(GetIRContext(),
|
||||
*GetFactManager())) {
|
||||
if (candidate_transformation.IsApplicable(
|
||||
GetIRContext(), *GetTransformationContext())) {
|
||||
// Only consider a transformation as a candidate if it is applicable.
|
||||
candidate_transformations.push_back(
|
||||
std::move(candidate_transformation));
|
||||
|
@ -109,10 +110,11 @@ void FuzzerPassAddDeadBreaks::Apply() {
|
|||
candidate_transformations.erase(candidate_transformations.begin() + index);
|
||||
// Probabilistically decide whether to try to apply it vs. ignore it, in the
|
||||
// case that it is applicable.
|
||||
if (transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ namespace fuzz {
|
|||
// A fuzzer pass for adding dead break edges to the module.
|
||||
class FuzzerPassAddDeadBreaks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassAddDeadBreaks(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddDeadContinues::~FuzzerPassAddDeadContinues() = default;
|
||||
|
||||
|
@ -75,10 +76,11 @@ void FuzzerPassAddDeadContinues::Apply() {
|
|||
// Probabilistically decide whether to apply the transformation in the
|
||||
// case that it is applicable.
|
||||
if (candidate_transformation.IsApplicable(GetIRContext(),
|
||||
*GetFactManager()) &&
|
||||
*GetTransformationContext()) &&
|
||||
GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
|
||||
candidate_transformation.Apply(GetIRContext(), GetFactManager());
|
||||
candidate_transformation.Apply(GetIRContext(),
|
||||
GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
candidate_transformation.ToMessage();
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddDeadContinues : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddDeadContinues(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -23,10 +23,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() =
|
||||
default;
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddEquationInstructions : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddEquationInstructions(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -24,10 +24,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
|
||||
|
||||
|
@ -74,8 +75,9 @@ void FuzzerPassAddFunctionCalls::Apply() {
|
|||
while (!candidate_functions.empty()) {
|
||||
opt::Function* candidate_function =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions);
|
||||
if (!GetFactManager()->BlockIsDead(block->id()) &&
|
||||
!GetFactManager()->FunctionIsLivesafe(
|
||||
if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
|
||||
block->id()) &&
|
||||
!GetTransformationContext()->GetFactManager()->FunctionIsLivesafe(
|
||||
candidate_function->result_id())) {
|
||||
// Unless in a dead block, only livesafe functions can be invoked
|
||||
continue;
|
||||
|
@ -132,9 +134,11 @@ FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters(
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
if (!GetFactManager()->BlockIsDead(block->id()) &&
|
||||
!GetFactManager()->PointeeValueIsIrrelevant(
|
||||
inst->result_id())) {
|
||||
if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
|
||||
block->id()) &&
|
||||
!GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->PointeeValueIsIrrelevant(inst->result_id())) {
|
||||
// We can only pass a pointer as an actual parameter
|
||||
// if the pointee value for the pointer is irrelevant,
|
||||
// or if the block from which we would make the
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddFunctionCalls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddFunctionCalls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -21,53 +21,54 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
|
||||
|
||||
void FuzzerPassAddGlobalVariables::Apply() {
|
||||
auto base_type_ids_and_pointers =
|
||||
GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate);
|
||||
auto basic_type_ids_and_pointers =
|
||||
GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate);
|
||||
|
||||
// These are the base types that are available to this fuzzer pass.
|
||||
auto& base_types = base_type_ids_and_pointers.first;
|
||||
// These are the basic types that are available to this fuzzer pass.
|
||||
auto& basic_types = basic_type_ids_and_pointers.first;
|
||||
|
||||
// These are the pointers to those base types that are *initially* available
|
||||
// These are the pointers to those basic types that are *initially* available
|
||||
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
|
||||
// none are available for a given base type.
|
||||
auto& base_type_to_pointers = base_type_ids_and_pointers.second;
|
||||
// none are available for a given basic type.
|
||||
auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
|
||||
|
||||
// Probabilistically keep adding global variables.
|
||||
while (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
|
||||
// Choose a random base type; the new variable's type will be a pointer to
|
||||
// this base type.
|
||||
uint32_t base_type =
|
||||
base_types[GetFuzzerContext()->RandomIndex(base_types)];
|
||||
// Choose a random basic type; the new variable's type will be a pointer to
|
||||
// this basic type.
|
||||
uint32_t basic_type =
|
||||
basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
|
||||
uint32_t pointer_type_id;
|
||||
std::vector<uint32_t>& available_pointers_to_base_type =
|
||||
base_type_to_pointers.at(base_type);
|
||||
// Determine whether there is at least one pointer to this base type.
|
||||
if (available_pointers_to_base_type.empty()) {
|
||||
std::vector<uint32_t>& available_pointers_to_basic_type =
|
||||
basic_type_to_pointers.at(basic_type);
|
||||
// Determine whether there is at least one pointer to this basic type.
|
||||
if (available_pointers_to_basic_type.empty()) {
|
||||
// There is not. Make one, to use here, and add it to the available
|
||||
// pointers for the base type so that future variables can potentially
|
||||
// pointers for the basic type so that future variables can potentially
|
||||
// use it.
|
||||
pointer_type_id = GetFuzzerContext()->GetFreshId();
|
||||
available_pointers_to_base_type.push_back(pointer_type_id);
|
||||
available_pointers_to_basic_type.push_back(pointer_type_id);
|
||||
ApplyTransformation(TransformationAddTypePointer(
|
||||
pointer_type_id, SpvStorageClassPrivate, base_type));
|
||||
pointer_type_id, SpvStorageClassPrivate, basic_type));
|
||||
} else {
|
||||
// There is - grab one.
|
||||
pointer_type_id =
|
||||
available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
|
||||
available_pointers_to_base_type)];
|
||||
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
|
||||
available_pointers_to_basic_type)];
|
||||
}
|
||||
ApplyTransformation(TransformationAddGlobalVariable(
|
||||
GetFuzzerContext()->GetFreshId(), pointer_type_id,
|
||||
FindOrCreateZeroConstant(base_type), true));
|
||||
FindOrCreateZeroConstant(basic_type), true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddGlobalVariables : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddGlobalVariables(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddLoads::FuzzerPassAddLoads(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ namespace fuzz {
|
|||
// Fuzzer pass that adds stores, at random, from pointers in the module.
|
||||
class FuzzerPassAddLoads : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddLoads(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassAddLoads(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -22,55 +22,56 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
|
||||
|
||||
void FuzzerPassAddLocalVariables::Apply() {
|
||||
auto base_type_ids_and_pointers =
|
||||
GetAvailableBaseTypesAndPointers(SpvStorageClassFunction);
|
||||
auto basic_type_ids_and_pointers =
|
||||
GetAvailableBasicTypesAndPointers(SpvStorageClassFunction);
|
||||
|
||||
// These are the base types that are available to this fuzzer pass.
|
||||
auto& base_types = base_type_ids_and_pointers.first;
|
||||
// These are the basic types that are available to this fuzzer pass.
|
||||
auto& basic_types = basic_type_ids_and_pointers.first;
|
||||
|
||||
// These are the pointers to those base types that are *initially* available
|
||||
// These are the pointers to those basic types that are *initially* available
|
||||
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
|
||||
// none are available for a given base type.
|
||||
auto& base_type_to_pointers = base_type_ids_and_pointers.second;
|
||||
// none are available for a given basic type.
|
||||
auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
|
||||
|
||||
// Consider every function in the module.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
// Probabilistically keep adding random variables to this function.
|
||||
while (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
|
||||
// Choose a random base type; the new variable's type will be a pointer to
|
||||
// this base type.
|
||||
uint32_t base_type =
|
||||
base_types[GetFuzzerContext()->RandomIndex(base_types)];
|
||||
// Choose a random basic type; the new variable's type will be a pointer
|
||||
// to this basic type.
|
||||
uint32_t basic_type =
|
||||
basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
|
||||
uint32_t pointer_type;
|
||||
std::vector<uint32_t>& available_pointers_to_base_type =
|
||||
base_type_to_pointers.at(base_type);
|
||||
// Determine whether there is at least one pointer to this base type.
|
||||
if (available_pointers_to_base_type.empty()) {
|
||||
std::vector<uint32_t>& available_pointers_to_basic_type =
|
||||
basic_type_to_pointers.at(basic_type);
|
||||
// Determine whether there is at least one pointer to this basic type.
|
||||
if (available_pointers_to_basic_type.empty()) {
|
||||
// There is not. Make one, to use here, and add it to the available
|
||||
// pointers for the base type so that future variables can potentially
|
||||
// pointers for the basic type so that future variables can potentially
|
||||
// use it.
|
||||
pointer_type = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddTypePointer(
|
||||
pointer_type, SpvStorageClassFunction, base_type));
|
||||
available_pointers_to_base_type.push_back(pointer_type);
|
||||
pointer_type, SpvStorageClassFunction, basic_type));
|
||||
available_pointers_to_basic_type.push_back(pointer_type);
|
||||
} else {
|
||||
// There is - grab one.
|
||||
pointer_type =
|
||||
available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
|
||||
available_pointers_to_base_type)];
|
||||
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
|
||||
available_pointers_to_basic_type)];
|
||||
}
|
||||
ApplyTransformation(TransformationAddLocalVariable(
|
||||
GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
|
||||
FindOrCreateZeroConstant(base_type), true));
|
||||
FindOrCreateZeroConstant(basic_type), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddLocalVariables : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddLocalVariables(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -20,10 +20,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddNoContractionDecorations::
|
||||
~FuzzerPassAddNoContractionDecorations() = default;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddNoContractionDecorations : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddNoContractionDecorations(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddStores::FuzzerPassAddStores(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddStores::~FuzzerPassAddStores() = default;
|
||||
|
||||
|
@ -82,9 +83,13 @@ void FuzzerPassAddStores::Apply() {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
return GetFactManager()->BlockIsDead(block->id()) ||
|
||||
GetFactManager()->PointeeValueIsIrrelevant(
|
||||
instruction->result_id());
|
||||
return GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->BlockIsDead(block->id()) ||
|
||||
GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->PointeeValueIsIrrelevant(
|
||||
instruction->result_id());
|
||||
});
|
||||
|
||||
// At this point, |relevant_pointers| contains all the pointers we might
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace fuzz {
|
|||
// are known not to affect the module's overall behaviour.
|
||||
class FuzzerPassAddStores : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddStores(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassAddStores(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -25,10 +25,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddUsefulConstructs::~FuzzerPassAddUsefulConstructs() = default;
|
||||
|
||||
|
@ -49,9 +50,10 @@ void FuzzerPassAddUsefulConstructs::MaybeAddIntConstant(
|
|||
TransformationAddConstantScalar add_constant_int =
|
||||
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
|
||||
int_type_id, data);
|
||||
assert(add_constant_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
assert(add_constant_int.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Should be applicable by construction.");
|
||||
add_constant_int.Apply(GetIRContext(), GetFactManager());
|
||||
add_constant_int.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = add_constant_int.ToMessage();
|
||||
}
|
||||
}
|
||||
|
@ -75,9 +77,10 @@ void FuzzerPassAddUsefulConstructs::MaybeAddFloatConstant(
|
|||
TransformationAddConstantScalar add_constant_float =
|
||||
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
|
||||
float_type_id, data);
|
||||
assert(add_constant_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
assert(add_constant_float.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Should be applicable by construction.");
|
||||
add_constant_float.Apply(GetIRContext(), GetFactManager());
|
||||
add_constant_float.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
add_constant_float.ToMessage();
|
||||
}
|
||||
|
@ -90,9 +93,10 @@ void FuzzerPassAddUsefulConstructs::Apply() {
|
|||
if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) {
|
||||
auto add_type_boolean =
|
||||
TransformationAddTypeBoolean(GetFuzzerContext()->GetFreshId());
|
||||
assert(add_type_boolean.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
assert(add_type_boolean.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Should be applicable by construction.");
|
||||
add_type_boolean.Apply(GetIRContext(), GetFactManager());
|
||||
add_type_boolean.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
add_type_boolean.ToMessage();
|
||||
}
|
||||
|
@ -105,9 +109,10 @@ void FuzzerPassAddUsefulConstructs::Apply() {
|
|||
if (!GetIRContext()->get_type_mgr()->GetId(&temp_int_type)) {
|
||||
TransformationAddTypeInt add_type_int = TransformationAddTypeInt(
|
||||
GetFuzzerContext()->GetFreshId(), 32, is_signed);
|
||||
assert(add_type_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
assert(add_type_int.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Should be applicable by construction.");
|
||||
add_type_int.Apply(GetIRContext(), GetFactManager());
|
||||
add_type_int.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = add_type_int.ToMessage();
|
||||
}
|
||||
}
|
||||
|
@ -119,9 +124,10 @@ void FuzzerPassAddUsefulConstructs::Apply() {
|
|||
if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) {
|
||||
TransformationAddTypeFloat add_type_float =
|
||||
TransformationAddTypeFloat(GetFuzzerContext()->GetFreshId(), 32);
|
||||
assert(add_type_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
assert(add_type_float.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Should be applicable by construction.");
|
||||
add_type_float.Apply(GetIRContext(), GetFactManager());
|
||||
add_type_float.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = add_type_float.ToMessage();
|
||||
}
|
||||
}
|
||||
|
@ -139,9 +145,9 @@ void FuzzerPassAddUsefulConstructs::Apply() {
|
|||
TransformationAddConstantBoolean add_constant_boolean(
|
||||
GetFuzzerContext()->GetFreshId(), boolean_value);
|
||||
assert(add_constant_boolean.IsApplicable(GetIRContext(),
|
||||
*GetFactManager()) &&
|
||||
*GetTransformationContext()) &&
|
||||
"Should be applicable by construction.");
|
||||
add_constant_boolean.Apply(GetIRContext(), GetFactManager());
|
||||
add_constant_boolean.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
add_constant_boolean.ToMessage();
|
||||
}
|
||||
|
@ -168,8 +174,9 @@ void FuzzerPassAddUsefulConstructs::Apply() {
|
|||
// of the element
|
||||
// - a signed integer constant for each index required to access the element
|
||||
// - a constant for the constant value itself
|
||||
for (auto& fact_and_type_id :
|
||||
GetFactManager()->GetConstantUniformFactsAndTypes()) {
|
||||
for (auto& fact_and_type_id : GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->GetConstantUniformFactsAndTypes()) {
|
||||
uint32_t element_type_id = fact_and_type_id.second;
|
||||
assert(element_type_id);
|
||||
auto element_type =
|
||||
|
@ -183,9 +190,10 @@ void FuzzerPassAddUsefulConstructs::Apply() {
|
|||
auto add_pointer =
|
||||
TransformationAddTypePointer(GetFuzzerContext()->GetFreshId(),
|
||||
SpvStorageClassUniform, element_type_id);
|
||||
assert(add_pointer.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
assert(add_pointer.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Should be applicable by construction.");
|
||||
add_pointer.Apply(GetIRContext(), GetFactManager());
|
||||
add_pointer.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = add_pointer.ToMessage();
|
||||
}
|
||||
std::vector<uint32_t> words;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassAddUsefulConstructs : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddUsefulConstructs(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -20,10 +20,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace fuzz {
|
|||
class FuzzerPassAdjustFunctionControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustFunctionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -20,10 +20,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace fuzz {
|
|||
class FuzzerPassAdjustLoopControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustLoopControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() =
|
||||
default;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustMemoryOperandsMasks(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -20,10 +20,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
|
||||
default;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace fuzz {
|
|||
class FuzzerPassAdjustSelectionControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustSelectionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -25,26 +25,33 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default;
|
||||
|
||||
void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
for (auto id_with_known_synonyms :
|
||||
GetFactManager()->GetIdsForWhichSynonymsAreKnown(GetIRContext())) {
|
||||
// Gather up all uses of |id_with_known_synonym|, and then subsequently
|
||||
// iterate over these uses. We use this separation because, when
|
||||
// considering a given use, we might apply a transformation that will
|
||||
GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->GetIdsForWhichSynonymsAreKnown(GetIRContext())) {
|
||||
// Gather up all uses of |id_with_known_synonym| as a regular id, and
|
||||
// subsequently iterate over these uses. We use this separation because,
|
||||
// when considering a given use, we might apply a transformation that will
|
||||
// invalidate the def-use manager.
|
||||
std::vector<std::pair<opt::Instruction*, uint32_t>> uses;
|
||||
GetIRContext()->get_def_use_mgr()->ForEachUse(
|
||||
id_with_known_synonyms,
|
||||
[&uses](opt::Instruction* use_inst, uint32_t use_index) -> void {
|
||||
uses.emplace_back(
|
||||
std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
|
||||
// We only gather up regular id uses; e.g. we do not include a use of
|
||||
// the id as the scope for an atomic operation.
|
||||
if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) {
|
||||
uses.emplace_back(
|
||||
std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
|
||||
}
|
||||
});
|
||||
|
||||
for (auto& use : uses) {
|
||||
|
@ -70,7 +77,8 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
|||
}
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
|
||||
for (auto& data_descriptor : GetFactManager()->GetSynonymsForId(
|
||||
for (auto& data_descriptor :
|
||||
GetTransformationContext()->GetFactManager()->GetSynonymsForId(
|
||||
id_with_known_synonyms, GetIRContext())) {
|
||||
protobufs::DataDescriptor descriptor_for_this_id =
|
||||
MakeDataDescriptor(id_with_known_synonyms, {});
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace fuzz {
|
|||
class FuzzerPassApplyIdSynonyms : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context,
|
||||
FactManager* fact_manager,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -25,10 +25,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassConstructComposites::FuzzerPassConstructComposites(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
|
||||
|
||||
|
@ -87,7 +88,7 @@ void FuzzerPassConstructComposites::Apply() {
|
|||
// for constructing a composite of that type. Otherwise these variables
|
||||
// will remain 0 and null respectively.
|
||||
uint32_t chosen_composite_type = 0;
|
||||
std::unique_ptr<std::vector<uint32_t>> constructor_arguments = nullptr;
|
||||
std::vector<uint32_t> constructor_arguments;
|
||||
|
||||
// Initially, all composite type ids are available for us to try. Keep
|
||||
// trying until we run out of options.
|
||||
|
@ -95,35 +96,38 @@ void FuzzerPassConstructComposites::Apply() {
|
|||
while (!composites_to_try_constructing.empty()) {
|
||||
// Remove a composite type from the composite types left for us to
|
||||
// try.
|
||||
auto index =
|
||||
GetFuzzerContext()->RandomIndex(composites_to_try_constructing);
|
||||
auto next_composite_to_try_constructing =
|
||||
composites_to_try_constructing[index];
|
||||
composites_to_try_constructing.erase(
|
||||
composites_to_try_constructing.begin() + index);
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(
|
||||
&composites_to_try_constructing);
|
||||
|
||||
// Now try to construct a composite of this type, using an appropriate
|
||||
// helper method depending on the kind of composite type.
|
||||
auto composite_type = GetIRContext()->get_type_mgr()->GetType(
|
||||
auto composite_type_inst = GetIRContext()->get_def_use_mgr()->GetDef(
|
||||
next_composite_to_try_constructing);
|
||||
if (auto array_type = composite_type->AsArray()) {
|
||||
constructor_arguments = TryConstructingArrayComposite(
|
||||
*array_type, type_id_to_available_instructions);
|
||||
} else if (auto matrix_type = composite_type->AsMatrix()) {
|
||||
constructor_arguments = TryConstructingMatrixComposite(
|
||||
*matrix_type, type_id_to_available_instructions);
|
||||
} else if (auto struct_type = composite_type->AsStruct()) {
|
||||
constructor_arguments = TryConstructingStructComposite(
|
||||
*struct_type, type_id_to_available_instructions);
|
||||
} else {
|
||||
auto vector_type = composite_type->AsVector();
|
||||
assert(vector_type &&
|
||||
"The space of possible composite types should be covered by "
|
||||
"the above cases.");
|
||||
constructor_arguments = TryConstructingVectorComposite(
|
||||
*vector_type, type_id_to_available_instructions);
|
||||
switch (composite_type_inst->opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
constructor_arguments = FindComponentsToConstructArray(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
case SpvOpTypeMatrix:
|
||||
constructor_arguments = FindComponentsToConstructMatrix(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
case SpvOpTypeStruct:
|
||||
constructor_arguments = FindComponentsToConstructStruct(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
case SpvOpTypeVector:
|
||||
constructor_arguments = FindComponentsToConstructVector(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
default:
|
||||
assert(false &&
|
||||
"The space of possible composite types should be covered "
|
||||
"by the above cases.");
|
||||
break;
|
||||
}
|
||||
if (constructor_arguments != nullptr) {
|
||||
if (!constructor_arguments.empty()) {
|
||||
// We succeeded! Note the composite type we finally settled on, and
|
||||
// exit from the loop.
|
||||
chosen_composite_type = next_composite_to_try_constructing;
|
||||
|
@ -134,20 +138,15 @@ void FuzzerPassConstructComposites::Apply() {
|
|||
if (!chosen_composite_type) {
|
||||
// We did not manage to make a composite; return 0 to indicate that no
|
||||
// instructions were added.
|
||||
assert(constructor_arguments == nullptr);
|
||||
assert(constructor_arguments.empty());
|
||||
return;
|
||||
}
|
||||
assert(constructor_arguments != nullptr);
|
||||
assert(!constructor_arguments.empty());
|
||||
|
||||
// Make and apply a transformation.
|
||||
TransformationCompositeConstruct transformation(
|
||||
chosen_composite_type, *constructor_arguments,
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId());
|
||||
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
"This transformation should be applicable by construction.");
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
*GetTransformations()->add_transformation() =
|
||||
transformation.ToMessage();
|
||||
ApplyTransformation(TransformationCompositeConstruct(
|
||||
chosen_composite_type, constructor_arguments,
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId()));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -160,20 +159,15 @@ void FuzzerPassConstructComposites::RecordAvailableInstruction(
|
|||
type_id_to_available_instructions->at(inst->type_id()).push_back(inst);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingArrayComposite(
|
||||
const opt::analysis::Array& array_type,
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructArray(
|
||||
const opt::Instruction& array_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
// At present we assume arrays have a constant size.
|
||||
assert(array_type.length_info().words.size() == 2);
|
||||
assert(array_type.length_info().words[0] ==
|
||||
opt::analysis::Array::LengthInfo::kConstant);
|
||||
|
||||
auto result = MakeUnique<std::vector<uint32_t>>();
|
||||
assert(array_type_instruction.opcode() == SpvOpTypeArray &&
|
||||
"Precondition: instruction must be an array type.");
|
||||
|
||||
// Get the element type for the array.
|
||||
auto element_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(array_type.element_type());
|
||||
auto element_type_id = array_type_instruction.GetSingleWordInOperand(0);
|
||||
|
||||
// Get all instructions at our disposal that compute something of this element
|
||||
// type.
|
||||
|
@ -184,26 +178,34 @@ FuzzerPassConstructComposites::TryConstructingArrayComposite(
|
|||
// If there are not any instructions available that compute the element type
|
||||
// of the array then we are not in a position to construct a composite with
|
||||
// this array type.
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
for (uint32_t index = 0; index < array_type.length_info().words[1]; index++) {
|
||||
result->push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
|
||||
uint32_t array_length =
|
||||
GetIRContext()
|
||||
->get_def_use_mgr()
|
||||
->GetDef(array_type_instruction.GetSingleWordInOperand(1))
|
||||
->GetSingleWordInOperand(0);
|
||||
|
||||
std::vector<uint32_t> result;
|
||||
for (uint32_t index = 0; index < array_length; index++) {
|
||||
result.push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingMatrixComposite(
|
||||
const opt::analysis::Matrix& matrix_type,
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructMatrix(
|
||||
const opt::Instruction& matrix_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
auto result = MakeUnique<std::vector<uint32_t>>();
|
||||
assert(matrix_type_instruction.opcode() == SpvOpTypeMatrix &&
|
||||
"Precondition: instruction must be a matrix type.");
|
||||
|
||||
// Get the element type for the matrix.
|
||||
auto element_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(matrix_type.element_type());
|
||||
auto element_type_id = matrix_type_instruction.GetSingleWordInOperand(0);
|
||||
|
||||
// Get all instructions at our disposal that compute something of this element
|
||||
// type.
|
||||
|
@ -214,25 +216,32 @@ FuzzerPassConstructComposites::TryConstructingMatrixComposite(
|
|||
// If there are not any instructions available that compute the element type
|
||||
// of the matrix then we are not in a position to construct a composite with
|
||||
// this matrix type.
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
for (uint32_t index = 0; index < matrix_type.element_count(); index++) {
|
||||
result->push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
std::vector<uint32_t> result;
|
||||
for (uint32_t index = 0;
|
||||
index < matrix_type_instruction.GetSingleWordInOperand(1); index++) {
|
||||
result.push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingStructComposite(
|
||||
const opt::analysis::Struct& struct_type,
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructStruct(
|
||||
const opt::Instruction& struct_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
auto result = MakeUnique<std::vector<uint32_t>>();
|
||||
assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
|
||||
"Precondition: instruction must be a struct type.");
|
||||
std::vector<uint32_t> result;
|
||||
// Consider the type of each field of the struct.
|
||||
for (auto element_type : struct_type.element_types()) {
|
||||
auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
|
||||
for (uint32_t in_operand_index = 0;
|
||||
in_operand_index < struct_type_instruction.NumInOperands();
|
||||
in_operand_index++) {
|
||||
auto element_type_id =
|
||||
struct_type_instruction.GetSingleWordInOperand(in_operand_index);
|
||||
// Find the instructions at our disposal that compute something of the field
|
||||
// type.
|
||||
auto available_instructions =
|
||||
|
@ -240,24 +249,28 @@ FuzzerPassConstructComposites::TryConstructingStructComposite(
|
|||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
// If there are no such instructions, we cannot construct a composite of
|
||||
// this struct type.
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
result->push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
result.push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingVectorComposite(
|
||||
const opt::analysis::Vector& vector_type,
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructVector(
|
||||
const opt::Instruction& vector_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
assert(vector_type_instruction.opcode() == SpvOpTypeVector &&
|
||||
"Precondition: instruction must be a vector type.");
|
||||
|
||||
// Get details of the type underlying the vector, and the width of the vector,
|
||||
// for convenience.
|
||||
auto element_type = vector_type.element_type();
|
||||
auto element_count = vector_type.element_count();
|
||||
auto element_type_id = vector_type_instruction.GetSingleWordInOperand(0);
|
||||
auto element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
|
||||
auto element_count = vector_type_instruction.GetSingleWordInOperand(1);
|
||||
|
||||
// Collect a mapping, from type id to width, for scalar/vector types that are
|
||||
// smaller in width than |vector_type|, but that have the same underlying
|
||||
|
@ -268,14 +281,12 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite(
|
|||
std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
|
||||
// Add the underlying type. This id must exist, in order for |vector_type| to
|
||||
// exist.
|
||||
auto scalar_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
|
||||
smaller_vector_type_id_to_width[scalar_type_id] = 1;
|
||||
smaller_vector_type_id_to_width[element_type_id] = 1;
|
||||
|
||||
// Now add every vector type with width at least 2, and less than the width of
|
||||
// |vector_type|.
|
||||
for (uint32_t width = 2; width < element_count; width++) {
|
||||
opt::analysis::Vector smaller_vector_type(vector_type.element_type(),
|
||||
width);
|
||||
opt::analysis::Vector smaller_vector_type(element_type, width);
|
||||
auto smaller_vector_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
|
||||
// We might find that there is no declared type of this smaller width.
|
||||
|
@ -302,12 +313,11 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite(
|
|||
// order at this stage.
|
||||
std::vector<opt::Instruction*> instructions_to_use;
|
||||
|
||||
while (vector_slots_used < vector_type.element_count()) {
|
||||
while (vector_slots_used < element_count) {
|
||||
std::vector<opt::Instruction*> instructions_to_choose_from;
|
||||
for (auto& entry : smaller_vector_type_id_to_width) {
|
||||
if (entry.second >
|
||||
std::min(vector_type.element_count() - 1,
|
||||
vector_type.element_count() - vector_slots_used)) {
|
||||
std::min(element_count - 1, element_count - vector_slots_used)) {
|
||||
continue;
|
||||
}
|
||||
auto available_instructions =
|
||||
|
@ -326,7 +336,7 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite(
|
|||
// another manner, so we could opt to retry a few times here, but it is
|
||||
// simpler to just give up on the basis that this will not happen
|
||||
// frequently.
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
auto instruction_to_use =
|
||||
instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
|
||||
|
@ -345,16 +355,16 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite(
|
|||
vector_slots_used += 1;
|
||||
}
|
||||
}
|
||||
assert(vector_slots_used == vector_type.element_count());
|
||||
assert(vector_slots_used == element_count);
|
||||
|
||||
auto result = MakeUnique<std::vector<uint32_t>>();
|
||||
std::vector<uint32_t> result;
|
||||
std::vector<uint32_t> operands;
|
||||
while (!instructions_to_use.empty()) {
|
||||
auto index = GetFuzzerContext()->RandomIndex(instructions_to_use);
|
||||
result->push_back(instructions_to_use[index]->result_id());
|
||||
result.push_back(instructions_to_use[index]->result_id());
|
||||
instructions_to_use.erase(instructions_to_use.begin() + index);
|
||||
}
|
||||
assert(result->size() > 1);
|
||||
assert(result.size() > 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace fuzz {
|
|||
class FuzzerPassConstructComposites : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassConstructComposites(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
@ -49,27 +49,28 @@ class FuzzerPassConstructComposites : public FuzzerPass {
|
|||
opt::Instruction* inst,
|
||||
TypeIdToInstructions* type_id_to_available_instructions);
|
||||
|
||||
// Requires that |array_type_instruction| has opcode OpTypeArray.
|
||||
// Attempts to find suitable instruction result ids from the values of
|
||||
// |type_id_to_available_instructions| that would allow a composite of type
|
||||
// |array_type| to be constructed. Returns said ids if they can be found.
|
||||
// Returns |nullptr| otherwise.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingArrayComposite(
|
||||
const opt::analysis::Array& array_type,
|
||||
// |array_type_instruction| to be constructed. Returns said ids if they can
|
||||
// be found and an empty vector otherwise.
|
||||
std::vector<uint32_t> FindComponentsToConstructArray(
|
||||
const opt::Instruction& array_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to TryConstructingArrayComposite, but for matrices.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingMatrixComposite(
|
||||
const opt::analysis::Matrix& matrix_type,
|
||||
// Similar to FindComponentsToConstructArray, but for matrices.
|
||||
std::vector<uint32_t> FindComponentsToConstructMatrix(
|
||||
const opt::Instruction& matrix_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to TryConstructingArrayComposite, but for structs.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingStructComposite(
|
||||
const opt::analysis::Struct& struct_type,
|
||||
// Similar to FindComponentsToConstructArray, but for structs.
|
||||
std::vector<uint32_t> FindComponentsToConstructStruct(
|
||||
const opt::Instruction& struct_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to TryConstructingArrayComposite, but for vectors.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingVectorComposite(
|
||||
const opt::analysis::Vector& vector_type,
|
||||
// Similar to FindComponentsToConstructArray, but for vectors.
|
||||
std::vector<uint32_t> FindComponentsToConstructVector(
|
||||
const opt::Instruction& vector_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
};
|
||||
|
||||
|
|
|
@ -21,10 +21,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassCopyObjects::FuzzerPassCopyObjects(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ namespace fuzz {
|
|||
// A fuzzer pass for adding adding copies of objects to the module.
|
||||
class FuzzerPassCopyObjects : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassCopyObjects(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassCopyObjects(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "source/fuzz/instruction_message.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_function.h"
|
||||
#include "source/fuzz/transformation_add_global_undef.h"
|
||||
|
@ -40,11 +41,12 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassDonateModules::FuzzerPassDonateModules(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations,
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations),
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations),
|
||||
donor_suppliers_(donor_suppliers) {}
|
||||
|
||||
FuzzerPassDonateModules::~FuzzerPassDonateModules() = default;
|
||||
|
@ -62,7 +64,9 @@ void FuzzerPassDonateModules::Apply() {
|
|||
std::unique_ptr<opt::IRContext> donor_ir_context = donor_suppliers_.at(
|
||||
GetFuzzerContext()->RandomIndex(donor_suppliers_))();
|
||||
assert(donor_ir_context != nullptr && "Supplying of donor failed");
|
||||
assert(fuzzerutil::IsValid(donor_ir_context.get()) &&
|
||||
assert(fuzzerutil::IsValid(
|
||||
donor_ir_context.get(),
|
||||
GetTransformationContext()->GetValidatorOptions()) &&
|
||||
"The donor module must be valid");
|
||||
// Donate the supplied module.
|
||||
//
|
||||
|
@ -391,6 +395,20 @@ void FuzzerPassDonateModules::HandleTypesAndValues(
|
|||
original_id_to_donated_id->at(type_or_value.type_id()),
|
||||
constituent_ids));
|
||||
} break;
|
||||
case SpvOpConstantNull: {
|
||||
if (!original_id_to_donated_id->count(type_or_value.type_id())) {
|
||||
// We did not donate the type associated with this null constant, so
|
||||
// we cannot donate the null constant.
|
||||
continue;
|
||||
}
|
||||
|
||||
// It is fine to have multiple OpConstantNull instructions of the same
|
||||
// type, so we just add this to the recipient module.
|
||||
new_result_id = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantNull(
|
||||
new_result_id,
|
||||
original_id_to_donated_id->at(type_or_value.type_id())));
|
||||
} break;
|
||||
case SpvOpVariable: {
|
||||
// This is a global variable that could have one of various storage
|
||||
// classes. However, we change all global variable pointer storage
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace fuzz {
|
|||
class FuzzerPassDonateModules : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassDonateModules(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations,
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers);
|
||||
|
|
|
@ -22,10 +22,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassMergeBlocks::FuzzerPassMergeBlocks(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default;
|
||||
|
||||
|
@ -44,7 +45,8 @@ void FuzzerPassMergeBlocks::Apply() {
|
|||
// For other blocks, we add a transformation to merge the block into its
|
||||
// predecessor if that transformation would be applicable.
|
||||
TransformationMergeBlocks transformation(block.id());
|
||||
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
potential_transformations.push_back(transformation);
|
||||
}
|
||||
}
|
||||
|
@ -54,8 +56,9 @@ void FuzzerPassMergeBlocks::Apply() {
|
|||
uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
|
||||
auto transformation = potential_transformations.at(index);
|
||||
potential_transformations.erase(potential_transformations.begin() + index);
|
||||
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ namespace fuzz {
|
|||
// A fuzzer pass for merging blocks in the module.
|
||||
class FuzzerPassMergeBlocks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassMergeBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassMergeBlocks(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -25,10 +25,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default;
|
||||
|
||||
|
@ -83,12 +84,13 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
|
|||
bool_constant_use, lhs_id, rhs_id, comparison_opcode,
|
||||
GetFuzzerContext()->GetFreshId());
|
||||
// The transformation should be applicable by construction.
|
||||
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()));
|
||||
assert(
|
||||
transformation.IsApplicable(GetIRContext(), *GetTransformationContext()));
|
||||
|
||||
// Applying this transformation yields a pointer to the new instruction that
|
||||
// computes the result of the binary expression.
|
||||
auto binary_operator_instruction =
|
||||
transformation.ApplyWithResult(GetIRContext(), GetFactManager());
|
||||
auto binary_operator_instruction = transformation.ApplyWithResult(
|
||||
GetIRContext(), GetTransformationContext());
|
||||
|
||||
// Add this transformation to the sequence of transformations that have been
|
||||
// applied.
|
||||
|
@ -245,7 +247,9 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
|
|||
// with uniforms of the same value.
|
||||
|
||||
auto available_types_with_uniforms =
|
||||
GetFactManager()->GetTypesForWhichUniformValuesAreKnown();
|
||||
GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->GetTypesForWhichUniformValuesAreKnown();
|
||||
if (available_types_with_uniforms.empty()) {
|
||||
// Do not try to obfuscate if we do not have access to any uniform
|
||||
// elements with known values.
|
||||
|
@ -254,9 +258,10 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
|
|||
auto chosen_type_id =
|
||||
available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
|
||||
available_types_with_uniforms)];
|
||||
auto available_constants =
|
||||
GetFactManager()->GetConstantsAvailableFromUniformsForType(
|
||||
GetIRContext(), chosen_type_id);
|
||||
auto available_constants = GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->GetConstantsAvailableFromUniformsForType(
|
||||
GetIRContext(), chosen_type_id);
|
||||
if (available_constants.size() == 1) {
|
||||
// TODO(afd): for now we only obfuscate a boolean if there are at least
|
||||
// two constants available from uniforms, so that we can do a
|
||||
|
@ -308,8 +313,11 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
|
|||
|
||||
// Check whether we know that any uniforms are guaranteed to be equal to the
|
||||
// scalar constant associated with |constant_use|.
|
||||
auto uniform_descriptors = GetFactManager()->GetUniformDescriptorsForConstant(
|
||||
GetIRContext(), constant_use.id_of_interest());
|
||||
auto uniform_descriptors =
|
||||
GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->GetUniformDescriptorsForConstant(GetIRContext(),
|
||||
constant_use.id_of_interest());
|
||||
if (uniform_descriptors.empty()) {
|
||||
// No relevant uniforms, so do not obfuscate.
|
||||
return;
|
||||
|
@ -324,8 +332,9 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
|
|||
constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
|
||||
GetFuzzerContext()->GetFreshId());
|
||||
// Transformation should be applicable by construction.
|
||||
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()));
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
assert(
|
||||
transformation.IsApplicable(GetIRContext(), *GetTransformationContext()));
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace fuzz {
|
|||
class FuzzerPassObfuscateConstants : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassObfuscateConstants(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -23,10 +23,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default;
|
||||
|
||||
|
@ -88,8 +89,9 @@ void FuzzerPassOutlineFunctions::Apply() {
|
|||
/*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
|
||||
/*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
|
||||
/*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
|
||||
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassOutlineFunctions : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassOutlineFunctions(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -20,10 +20,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassPermuteBlocks::~FuzzerPassPermuteBlocks() = default;
|
||||
|
||||
|
@ -66,8 +67,9 @@ void FuzzerPassPermuteBlocks::Apply() {
|
|||
// down indefinitely.
|
||||
while (true) {
|
||||
TransformationMoveBlockDown transformation(*id);
|
||||
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
transformation.ToMessage();
|
||||
} else {
|
||||
|
|
|
@ -24,7 +24,8 @@ namespace fuzz {
|
|||
// manner.
|
||||
class FuzzerPassPermuteBlocks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassPermuteBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassPermuteBlocks(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -25,10 +25,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() =
|
||||
default;
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace fuzz {
|
|||
class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassPermuteFunctionParameters(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -23,10 +23,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassSplitBlocks::FuzzerPassSplitBlocks(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassSplitBlocks::~FuzzerPassSplitBlocks() = default;
|
||||
|
||||
|
@ -95,8 +96,9 @@ void FuzzerPassSplitBlocks::Apply() {
|
|||
// If the position we have chosen turns out to be a valid place to split
|
||||
// the block, we apply the split. Otherwise the block just doesn't get
|
||||
// split.
|
||||
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ namespace fuzz {
|
|||
// can be very useful for giving other passes a chance to apply.
|
||||
class FuzzerPassSplitBlocks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassSplitBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerPassSplitBlocks(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -22,10 +22,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace fuzz {
|
|||
class FuzzerPassSwapCommutableOperands : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassSwapCommutableOperands(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -22,10 +22,11 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassToggleAccessChainInstruction::
|
||||
~FuzzerPassToggleAccessChainInstruction() = default;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace fuzz {
|
|||
class FuzzerPassToggleAccessChainInstruction : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassToggleAccessChainInstruction(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
|
|
|
@ -329,11 +329,11 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
|
|||
return array_length_constant->GetU32();
|
||||
}
|
||||
|
||||
bool IsValid(opt::IRContext* context) {
|
||||
bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
|
||||
std::vector<uint32_t> binary;
|
||||
context->module()->ToBinary(&binary, false);
|
||||
SpirvTools tools(context->grammar().target_env());
|
||||
return tools.Validate(binary);
|
||||
return tools.Validate(binary.data(), binary.size(), validator_options);
|
||||
}
|
||||
|
||||
std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
|
||||
|
@ -537,6 +537,13 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool IsNullConstantSupported(const opt::analysis::Type& type) {
|
||||
return type.AsBool() || type.AsInteger() || type.AsFloat() ||
|
||||
type.AsMatrix() || type.AsVector() || type.AsArray() ||
|
||||
type.AsStruct() || type.AsPointer() || type.AsEvent() ||
|
||||
type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
|
|
@ -132,8 +132,9 @@ uint32_t GetNumberOfStructMembers(
|
|||
uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Returns true if and only if |context| is valid, according to the validator.
|
||||
bool IsValid(opt::IRContext* context);
|
||||
// Returns true if and only if |context| is valid, according to the validator
|
||||
// instantiated with |validator_options|.
|
||||
bool IsValid(opt::IRContext* context, spv_validator_options validator_options);
|
||||
|
||||
// Returns a clone of |context|, by writing |context| to a binary and then
|
||||
// parsing it again.
|
||||
|
@ -209,6 +210,10 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
|
|||
uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
|
||||
SpvStorageClass storage_class);
|
||||
|
||||
// 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);
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
|
|
@ -372,6 +372,7 @@ message Transformation {
|
|||
TransformationSwapCommutableOperands swap_commutable_operands = 41;
|
||||
TransformationPermuteFunctionParameters permute_function_parameters = 42;
|
||||
TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
|
||||
TransformationAddConstantNull add_constant_null = 44;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
|
@ -422,6 +423,18 @@ message TransformationAddConstantComposite {
|
|||
|
||||
}
|
||||
|
||||
message TransformationAddConstantNull {
|
||||
|
||||
// Adds a null constant.
|
||||
|
||||
// Id for the constant
|
||||
uint32 fresh_id = 1;
|
||||
|
||||
// Type of the constant
|
||||
uint32 type_id = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddConstantScalar {
|
||||
|
||||
// Adds a constant of the given scalar type.
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "source/fuzz/transformation_add_type_float.h"
|
||||
#include "source/fuzz/transformation_add_type_int.h"
|
||||
#include "source/fuzz/transformation_add_type_pointer.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/fuzz/transformation_move_block_down.h"
|
||||
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
|
@ -37,18 +38,22 @@ namespace spvtools {
|
|||
namespace fuzz {
|
||||
|
||||
struct Replayer::Impl {
|
||||
explicit Impl(spv_target_env env, bool validate)
|
||||
: target_env(env), validate_during_replay(validate) {}
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
Impl(spv_target_env env, bool validate, spv_validator_options options)
|
||||
: target_env(env),
|
||||
validate_during_replay(validate),
|
||||
validator_options(options) {}
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
const bool validate_during_replay; // Controls whether the validator should
|
||||
// be run after every replay step.
|
||||
spv_validator_options validator_options; // Options to control
|
||||
// validation
|
||||
};
|
||||
|
||||
Replayer::Replayer(spv_target_env env, bool validate_during_replay)
|
||||
: impl_(MakeUnique<Impl>(env, validate_during_replay)) {}
|
||||
Replayer::Replayer(spv_target_env env, bool validate_during_replay,
|
||||
spv_validator_options validator_options)
|
||||
: impl_(MakeUnique<Impl>(env, validate_during_replay, validator_options)) {}
|
||||
|
||||
Replayer::~Replayer() = default;
|
||||
|
||||
|
@ -74,7 +79,8 @@ Replayer::ReplayerResultStatus Replayer::Run(
|
|||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size())) {
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(),
|
||||
impl_->validator_options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return Replayer::ReplayerResultStatus::kInitialBinaryInvalid;
|
||||
|
@ -94,16 +100,19 @@ Replayer::ReplayerResultStatus Replayer::Run(
|
|||
|
||||
FactManager fact_manager;
|
||||
fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
impl_->validator_options);
|
||||
|
||||
// Consider the transformation proto messages in turn.
|
||||
for (auto& message : transformation_sequence_in.transformation()) {
|
||||
auto transformation = Transformation::FromMessage(message);
|
||||
|
||||
// Check whether the transformation can be applied.
|
||||
if (transformation->IsApplicable(ir_context.get(), fact_manager)) {
|
||||
if (transformation->IsApplicable(ir_context.get(),
|
||||
transformation_context)) {
|
||||
// The transformation is applicable, so apply it, and copy it to the
|
||||
// sequence of transformations that were applied.
|
||||
transformation->Apply(ir_context.get(), &fact_manager);
|
||||
transformation->Apply(ir_context.get(), &transformation_context);
|
||||
*transformation_sequence_out->add_transformation() = message;
|
||||
|
||||
if (impl_->validate_during_replay) {
|
||||
|
@ -111,8 +120,8 @@ Replayer::ReplayerResultStatus Replayer::Run(
|
|||
ir_context->module()->ToBinary(&binary_to_validate, false);
|
||||
|
||||
// Check whether the latest transformation led to a valid binary.
|
||||
if (!tools.Validate(&binary_to_validate[0],
|
||||
binary_to_validate.size())) {
|
||||
if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(),
|
||||
impl_->validator_options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during replay (set a "
|
||||
"breakpoint to inspect); stopping.");
|
||||
|
|
|
@ -37,7 +37,8 @@ class Replayer {
|
|||
};
|
||||
|
||||
// Constructs a replayer from the given target environment.
|
||||
explicit Replayer(spv_target_env env, bool validate_during_replay);
|
||||
Replayer(spv_target_env env, bool validate_during_replay,
|
||||
spv_validator_options validator_options);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
Replayer(const Replayer&) = delete;
|
||||
|
|
|
@ -60,20 +60,27 @@ protobufs::TransformationSequence RemoveChunk(
|
|||
} // namespace
|
||||
|
||||
struct Shrinker::Impl {
|
||||
explicit Impl(spv_target_env env, uint32_t limit, bool validate)
|
||||
: target_env(env), step_limit(limit), validate_during_replay(validate) {}
|
||||
Impl(spv_target_env env, uint32_t limit, bool validate,
|
||||
spv_validator_options options)
|
||||
: target_env(env),
|
||||
step_limit(limit),
|
||||
validate_during_replay(validate),
|
||||
validator_options(options) {}
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
const uint32_t step_limit; // Step limit for reductions.
|
||||
const bool validate_during_replay; // Determines whether to check for
|
||||
// validity during the replaying of
|
||||
// transformations.
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
const uint32_t step_limit; // Step limit for reductions.
|
||||
const bool validate_during_replay; // Determines whether to check for
|
||||
// validity during the replaying of
|
||||
// transformations.
|
||||
spv_validator_options validator_options; // Options to control validation.
|
||||
};
|
||||
|
||||
Shrinker::Shrinker(spv_target_env env, uint32_t step_limit,
|
||||
bool validate_during_replay)
|
||||
: impl_(MakeUnique<Impl>(env, step_limit, validate_during_replay)) {}
|
||||
bool validate_during_replay,
|
||||
spv_validator_options validator_options)
|
||||
: impl_(MakeUnique<Impl>(env, step_limit, validate_during_replay,
|
||||
validator_options)) {}
|
||||
|
||||
Shrinker::~Shrinker() = default;
|
||||
|
||||
|
@ -113,7 +120,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
|||
// succeeds, (b) get the binary that results from running these
|
||||
// transformations, and (c) get the subsequence of the initial transformations
|
||||
// that actually apply (in principle this could be a strict subsequence).
|
||||
if (Replayer(impl_->target_env, impl_->validate_during_replay)
|
||||
if (Replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options)
|
||||
.Run(binary_in, initial_facts, transformation_sequence_in,
|
||||
¤t_best_binary, ¤t_best_transformations) !=
|
||||
Replayer::ReplayerResultStatus::kComplete) {
|
||||
|
@ -184,7 +192,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
|||
// transformations inapplicable.
|
||||
std::vector<uint32_t> next_binary;
|
||||
protobufs::TransformationSequence next_transformation_sequence;
|
||||
if (Replayer(impl_->target_env, false)
|
||||
if (Replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options)
|
||||
.Run(binary_in, initial_facts, transformations_with_chunk_removed,
|
||||
&next_binary, &next_transformation_sequence) !=
|
||||
Replayer::ReplayerResultStatus::kComplete) {
|
||||
|
|
|
@ -50,8 +50,8 @@ class Shrinker {
|
|||
const std::vector<uint32_t>& binary, uint32_t counter)>;
|
||||
|
||||
// Constructs a shrinker from the given target environment.
|
||||
Shrinker(spv_target_env env, uint32_t step_limit,
|
||||
bool validate_during_replay);
|
||||
Shrinker(spv_target_env env, uint32_t step_limit, bool validate_during_replay,
|
||||
spv_validator_options validator_options);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
Shrinker(const Shrinker&) = delete;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "source/fuzz/transformation_access_chain.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_dead_block.h"
|
||||
#include "source/fuzz/transformation_add_dead_break.h"
|
||||
|
@ -78,6 +79,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
|||
case protobufs::Transformation::TransformationCase::kAddConstantComposite:
|
||||
return MakeUnique<TransformationAddConstantComposite>(
|
||||
message.add_constant_composite());
|
||||
case protobufs::Transformation::TransformationCase::kAddConstantNull:
|
||||
return MakeUnique<TransformationAddConstantNull>(
|
||||
message.add_constant_null());
|
||||
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
|
||||
return MakeUnique<TransformationAddConstantScalar>(
|
||||
message.add_constant_scalar());
|
||||
|
@ -195,9 +199,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
|||
}
|
||||
|
||||
bool Transformation::CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
uint32_t id, opt::IRContext* context,
|
||||
uint32_t id, opt::IRContext* ir_context,
|
||||
std::set<uint32_t>* ids_used_by_this_transformation) {
|
||||
if (!fuzzerutil::IsFreshId(context, id)) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, id)) {
|
||||
return false;
|
||||
}
|
||||
if (ids_used_by_this_transformation->count(id) != 0) {
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
|
@ -60,19 +60,22 @@ class Transformation {
|
|||
public:
|
||||
// A precondition that determines whether the transformation can be cleanly
|
||||
// applied in a semantics-preserving manner to the SPIR-V module given by
|
||||
// |context|, in the presence of facts captured by |fact_manager|.
|
||||
// |ir_context|, in the presence of facts and other contextual information
|
||||
// captured by |transformation_context|.
|
||||
//
|
||||
// Preconditions for individual transformations must be documented in the
|
||||
// associated header file using precise English. The fact manager is used to
|
||||
// provide access to facts about the module that are known to be true, on
|
||||
// associated header file using precise English. The transformation context
|
||||
// provides access to facts about the module that are known to be true, on
|
||||
// which the precondition may depend.
|
||||
virtual bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const = 0;
|
||||
virtual bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const = 0;
|
||||
|
||||
// Requires that IsApplicable(context, fact_manager) holds. Applies the
|
||||
// transformation, mutating |context| and possibly updating |fact_manager|
|
||||
// with new facts established by the transformation.
|
||||
virtual void Apply(opt::IRContext* context,
|
||||
FactManager* fact_manager) const = 0;
|
||||
// Requires that IsApplicable(ir_context, *transformation_context) holds.
|
||||
// Applies the transformation, mutating |ir_context| and possibly updating
|
||||
// |transformation_context| with new facts established by the transformation.
|
||||
virtual void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const = 0;
|
||||
|
||||
// Turns the transformation into a protobuf message for serialization.
|
||||
virtual protobufs::Transformation ToMessage() const = 0;
|
||||
|
@ -90,7 +93,7 @@ class Transformation {
|
|||
// checking id freshness for a transformation that uses many ids, all of which
|
||||
// must be distinct.
|
||||
static bool CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
uint32_t id, opt::IRContext* context,
|
||||
uint32_t id, opt::IRContext* ir_context,
|
||||
std::set<uint32_t>* ids_used_by_this_transformation);
|
||||
};
|
||||
|
||||
|
|
|
@ -40,19 +40,18 @@ TransformationAccessChain::TransformationAccessChain(
|
|||
}
|
||||
|
||||
bool TransformationAccessChain::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// The result id must be fresh
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
// The pointer id must exist and have a type.
|
||||
auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
|
||||
auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
|
||||
if (!pointer || !pointer->type_id()) {
|
||||
return false;
|
||||
}
|
||||
// The type must indeed be a pointer
|
||||
auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
|
||||
auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
|
||||
if (pointer_type->opcode() != SpvOpTypePointer) {
|
||||
return false;
|
||||
}
|
||||
|
@ -60,7 +59,7 @@ bool TransformationAccessChain::IsApplicable(
|
|||
// The described instruction to insert before must exist and be a suitable
|
||||
// point where an OpAccessChain instruction could be inserted.
|
||||
auto instruction_to_insert_before =
|
||||
FindInstruction(message_.instruction_to_insert_before(), context);
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context);
|
||||
if (!instruction_to_insert_before) {
|
||||
return false;
|
||||
}
|
||||
|
@ -86,7 +85,7 @@ bool TransformationAccessChain::IsApplicable(
|
|||
// The pointer on which the access chain is to be based needs to be available
|
||||
// (according to dominance rules) at the insertion point.
|
||||
if (!fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||
context, instruction_to_insert_before, message_.pointer_id())) {
|
||||
ir_context, instruction_to_insert_before, message_.pointer_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -104,7 +103,7 @@ bool TransformationAccessChain::IsApplicable(
|
|||
// integer. Otherwise, the integer with which the id is associated is the
|
||||
// second component.
|
||||
std::pair<bool, uint32_t> maybe_index_value =
|
||||
GetIndexValue(context, index_id);
|
||||
GetIndexValue(ir_context, index_id);
|
||||
if (!maybe_index_value.first) {
|
||||
// There was no integer: this index is no good.
|
||||
return false;
|
||||
|
@ -113,7 +112,7 @@ bool TransformationAccessChain::IsApplicable(
|
|||
// type is not a composite or the index is out of bounds, and the id of
|
||||
// the next type otherwise.
|
||||
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
|
||||
context, subobject_type_id, maybe_index_value.second);
|
||||
ir_context, subobject_type_id, maybe_index_value.second);
|
||||
if (!subobject_type_id) {
|
||||
// Either the type was not a composite (so that too many indices were
|
||||
// provided), or the index was out of bounds.
|
||||
|
@ -128,13 +127,14 @@ bool TransformationAccessChain::IsApplicable(
|
|||
// We do not use the type manager to look up this type, due to problems
|
||||
// associated with pointers to isomorphic structs being regarded as the same.
|
||||
return fuzzerutil::MaybeGetPointerType(
|
||||
context, subobject_type_id,
|
||||
ir_context, subobject_type_id,
|
||||
static_cast<SpvStorageClass>(
|
||||
pointer_type->GetSingleWordInOperand(0))) != 0;
|
||||
}
|
||||
|
||||
void TransformationAccessChain::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
// The operands to the access chain are the pointer followed by the indices.
|
||||
// The result type of the access chain is determined by where the indices
|
||||
// lead. We thus push the pointer to a sequence of operands, and then follow
|
||||
|
@ -148,8 +148,8 @@ void TransformationAccessChain::Apply(
|
|||
operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}});
|
||||
|
||||
// Start walking the indices, starting with the pointer's base type.
|
||||
auto pointer_type = context->get_def_use_mgr()->GetDef(
|
||||
context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
|
||||
auto pointer_type = ir_context->get_def_use_mgr()->GetDef(
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
|
||||
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
|
||||
|
||||
// Go through the index ids in turn.
|
||||
|
@ -157,33 +157,35 @@ void TransformationAccessChain::Apply(
|
|||
// Add the index id to the operands.
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
|
||||
// Get the integer value associated with the index id.
|
||||
uint32_t index_value = GetIndexValue(context, index_id).second;
|
||||
uint32_t index_value = GetIndexValue(ir_context, index_id).second;
|
||||
// Walk to the next type in the composite object using this index.
|
||||
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
|
||||
context, subobject_type_id, index_value);
|
||||
ir_context, subobject_type_id, index_value);
|
||||
}
|
||||
// The access chain's result type is a pointer to the composite component that
|
||||
// was reached after following all indices. The storage class is that of the
|
||||
// original pointer.
|
||||
uint32_t result_type = fuzzerutil::MaybeGetPointerType(
|
||||
context, subobject_type_id,
|
||||
ir_context, subobject_type_id,
|
||||
static_cast<SpvStorageClass>(pointer_type->GetSingleWordInOperand(0)));
|
||||
|
||||
// Add the access chain instruction to the module, and update the module's id
|
||||
// bound.
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
FindInstruction(message_.instruction_to_insert_before(), context)
|
||||
->InsertBefore(
|
||||
MakeUnique<opt::Instruction>(context, SpvOpAccessChain, result_type,
|
||||
message_.fresh_id(), operands));
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context)
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpAccessChain, result_type, message_.fresh_id(),
|
||||
operands));
|
||||
|
||||
// Conservatively invalidate all analyses.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
|
||||
// If the base pointer's pointee value was irrelevant, the same is true of the
|
||||
// pointee value of the result of this access chain.
|
||||
if (fact_manager->PointeeValueIsIrrelevant(message_.pointer_id())) {
|
||||
fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
|
||||
if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
message_.pointer_id())) {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,8 +196,8 @@ protobufs::Transformation TransformationAccessChain::ToMessage() const {
|
|||
}
|
||||
|
||||
std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
|
||||
opt::IRContext* context, uint32_t index_id) const {
|
||||
auto index_instruction = context->get_def_use_mgr()->GetDef(index_id);
|
||||
opt::IRContext* ir_context, uint32_t index_id) const {
|
||||
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could
|
||||
// allow non-constant indices when looking up non-structs, using clamping
|
||||
|
@ -203,7 +205,7 @@ std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
|
|||
return {false, 0};
|
||||
}
|
||||
auto index_type =
|
||||
context->get_def_use_mgr()->GetDef(index_instruction->type_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
|
||||
if (index_type->opcode() != SpvOpTypeInt ||
|
||||
index_type->GetSingleWordInOperand(0) != 32) {
|
||||
return {false, 0};
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -47,8 +47,9 @@ class TransformationAccessChain : public Transformation {
|
|||
// - If type t is the final type reached by walking these indices, the module
|
||||
// must include an instruction "OpTypePointer SC %t" where SC is the storage
|
||||
// class associated with |message_.pointer_id|
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds an instruction of the form:
|
||||
// |message_.fresh_id| = OpAccessChain %ptr |message_.index_id|
|
||||
|
@ -57,10 +58,12 @@ class TransformationAccessChain : public Transformation {
|
|||
// the indices in |message_.index_id|, and with the same storage class as
|
||||
// |message_.pointer_id|.
|
||||
//
|
||||
// If |fact_manager| reports that |message_.pointer_id| has an irrelevant
|
||||
// pointee value, then the fact that |message_.fresh_id| (the result of the
|
||||
// access chain) also has an irrelevant pointee value is also recorded.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
// If the fact manager in |transformation_context| reports that
|
||||
// |message_.pointer_id| has an irrelevant pointee value, then the fact that
|
||||
// |message_.fresh_id| (the result of the access chain) also has an irrelevant
|
||||
// pointee value is also recorded.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
@ -68,7 +71,7 @@ class TransformationAccessChain : public Transformation {
|
|||
// Returns {false, 0} if |index_id| does not correspond to a 32-bit integer
|
||||
// constant. Otherwise, returns {true, value}, where value is the value of
|
||||
// the 32-bit integer constant to which |index_id| corresponds.
|
||||
std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* context,
|
||||
std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
|
||||
uint32_t index_id) const;
|
||||
|
||||
protobufs::TransformationAccessChain message_;
|
||||
|
|
|
@ -31,27 +31,28 @@ TransformationAddConstantBoolean::TransformationAddConstantBoolean(
|
|||
}
|
||||
|
||||
bool TransformationAddConstantBoolean::IsApplicable(
|
||||
opt::IRContext* context, const FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::analysis::Bool bool_type;
|
||||
if (!context->get_type_mgr()->GetId(&bool_type)) {
|
||||
if (!ir_context->get_type_mgr()->GetId(&bool_type)) {
|
||||
// No OpTypeBool is present.
|
||||
return false;
|
||||
}
|
||||
return fuzzerutil::IsFreshId(context, message_.fresh_id());
|
||||
return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
|
||||
}
|
||||
|
||||
void TransformationAddConstantBoolean::Apply(opt::IRContext* context,
|
||||
FactManager* /*unused*/) const {
|
||||
void TransformationAddConstantBoolean::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::analysis::Bool bool_type;
|
||||
// Add the boolean constant to the module, ensuring the module's id bound is
|
||||
// high enough.
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
context->module()->AddGlobalValue(
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
ir_context->module()->AddGlobalValue(
|
||||
message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
|
||||
message_.fresh_id(), context->get_type_mgr()->GetId(&bool_type));
|
||||
message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type));
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const {
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -32,12 +32,14 @@ class TransformationAddConstantBoolean : public Transformation {
|
|||
|
||||
// - |message_.fresh_id| must not be used by the module.
|
||||
// - The module must already contain OpTypeBool.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// - Adds OpConstantTrue (OpConstantFalse) to the module with id
|
||||
// |message_.fresh_id| if |message_.is_true| holds (does not hold).
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
|
|
@ -37,15 +37,14 @@ TransformationAddConstantComposite::TransformationAddConstantComposite(
|
|||
}
|
||||
|
||||
bool TransformationAddConstantComposite::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// Check that the given id is fresh.
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
// Check that the composite type id is an instruction id.
|
||||
auto composite_type_instruction =
|
||||
context->get_def_use_mgr()->GetDef(message_.type_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.type_id());
|
||||
if (!composite_type_instruction) {
|
||||
return false;
|
||||
}
|
||||
|
@ -56,7 +55,7 @@ bool TransformationAddConstantComposite::IsApplicable(
|
|||
case SpvOpTypeArray:
|
||||
for (uint32_t index = 0;
|
||||
index <
|
||||
fuzzerutil::GetArraySize(*composite_type_instruction, context);
|
||||
fuzzerutil::GetArraySize(*composite_type_instruction, ir_context);
|
||||
index++) {
|
||||
constituent_type_ids.push_back(
|
||||
composite_type_instruction->GetSingleWordInOperand(0));
|
||||
|
@ -93,7 +92,7 @@ bool TransformationAddConstantComposite::IsApplicable(
|
|||
// corresponding constituent type.
|
||||
for (uint32_t index = 0; index < constituent_type_ids.size(); index++) {
|
||||
auto constituent_instruction =
|
||||
context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
|
||||
if (!constituent_instruction) {
|
||||
return false;
|
||||
}
|
||||
|
@ -105,18 +104,19 @@ bool TransformationAddConstantComposite::IsApplicable(
|
|||
}
|
||||
|
||||
void TransformationAddConstantComposite::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::Instruction::OperandList in_operands;
|
||||
for (auto constituent_id : message_.constituent_id()) {
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
|
||||
}
|
||||
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpConstantComposite, message_.type_id(), message_.fresh_id(),
|
||||
in_operands));
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpConstantComposite, message_.type_id(),
|
||||
message_.fresh_id(), in_operands));
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddConstantComposite::ToMessage()
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -38,13 +38,15 @@ class TransformationAddConstantComposite : public Transformation {
|
|||
// - |message_.type_id| must be the id of a composite type
|
||||
// - |message_.constituent_id| must refer to ids that match the constituent
|
||||
// types of this composite type
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds an OpConstantComposite instruction defining a constant of type
|
||||
// |message_.type_id|, using |message_.constituent_id| as constituents, with
|
||||
// result id |message_.fresh_id|.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// 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_add_constant_null.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationAddConstantNull::TransformationAddConstantNull(
|
||||
const spvtools::fuzz::protobufs::TransformationAddConstantNull& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id,
|
||||
uint32_t type_id) {
|
||||
message_.set_fresh_id(fresh_id);
|
||||
message_.set_type_id(type_id);
|
||||
}
|
||||
|
||||
bool TransformationAddConstantNull::IsApplicable(
|
||||
opt::IRContext* context, const TransformationContext& /*unused*/) const {
|
||||
// A fresh id is required.
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
auto type = context->get_type_mgr()->GetType(message_.type_id());
|
||||
// The type must exist.
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
// The type must be one of the types for which null constants are allowed,
|
||||
// according to the SPIR-V spec.
|
||||
return fuzzerutil::IsNullConstantSupported(*type);
|
||||
}
|
||||
|
||||
void TransformationAddConstantNull::Apply(
|
||||
opt::IRContext* context, TransformationContext* /*unused*/) const {
|
||||
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(),
|
||||
opt::Instruction::OperandList()));
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddConstantNull::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_constant_null() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
|
@ -0,0 +1,54 @@
|
|||
// 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_TRANSFORMATION_ADD_CONSTANT_NULL_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_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 TransformationAddConstantNull : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddConstantNull(
|
||||
const protobufs::TransformationAddConstantNull& message);
|
||||
|
||||
TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id);
|
||||
|
||||
// - |message_.fresh_id| must be fresh
|
||||
// - |message_.type_id| must be the id of a type for which it is acceptable
|
||||
// to create a null constant
|
||||
bool IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds an OpConstantNull instruction to the module, with |message_.type_id|
|
||||
// as its type. The instruction has result id |message_.fresh_id|.
|
||||
void Apply(opt::IRContext* context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddConstantNull message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_
|
|
@ -33,14 +33,13 @@ TransformationAddConstantScalar::TransformationAddConstantScalar(
|
|||
}
|
||||
|
||||
bool TransformationAddConstantScalar::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// The id needs to be fresh.
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
// The type id for the scalar must exist and be a type.
|
||||
auto type = context->get_type_mgr()->GetType(message_.type_id());
|
||||
auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
@ -61,20 +60,21 @@ bool TransformationAddConstantScalar::IsApplicable(
|
|||
}
|
||||
|
||||
void TransformationAddConstantScalar::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::Instruction::OperandList operand_list;
|
||||
for (auto word : message_.word()) {
|
||||
operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
|
||||
}
|
||||
context->module()->AddGlobalValue(
|
||||
MakeUnique<opt::Instruction>(context, SpvOpConstant, message_.type_id(),
|
||||
message_.fresh_id(), operand_list));
|
||||
ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(),
|
||||
operand_list));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddConstantScalar::ToMessage() const {
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -37,11 +37,13 @@ class TransformationAddConstantScalar : public Transformation {
|
|||
// - |message_.type_id| must be the id of a floating-point or integer type
|
||||
// - The size of |message_.word| must be compatible with the width of this
|
||||
// type
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds a new OpConstant instruction with the given type and words.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
|
|
@ -32,16 +32,15 @@ TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
|
|||
}
|
||||
|
||||
bool TransformationAddDeadBlock::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// The new block's id must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First, we check that a constant with the same value as
|
||||
// |message_.condition_value| is present.
|
||||
if (!fuzzerutil::MaybeGetBoolConstantId(context,
|
||||
if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
|
||||
message_.condition_value())) {
|
||||
// The required constant is not present, so the transformation cannot be
|
||||
// applied.
|
||||
|
@ -50,7 +49,7 @@ bool TransformationAddDeadBlock::IsApplicable(
|
|||
|
||||
// The existing block must indeed exist.
|
||||
auto existing_block =
|
||||
fuzzerutil::MaybeFindBlock(context, message_.existing_block());
|
||||
fuzzerutil::MaybeFindBlock(ir_context, message_.existing_block());
|
||||
if (!existing_block) {
|
||||
return false;
|
||||
}
|
||||
|
@ -68,13 +67,13 @@ bool TransformationAddDeadBlock::IsApplicable(
|
|||
// Its successor must not be a merge block nor continue target.
|
||||
auto successor_block_id =
|
||||
existing_block->terminator()->GetSingleWordInOperand(0);
|
||||
if (fuzzerutil::IsMergeOrContinue(context, successor_block_id)) {
|
||||
if (fuzzerutil::IsMergeOrContinue(ir_context, successor_block_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The successor must not be a loop header (i.e., |message_.existing_block|
|
||||
// must not be a back-edge block.
|
||||
if (context->cfg()->block(successor_block_id)->IsLoopHeader()) {
|
||||
if (ir_context->cfg()->block(successor_block_id)->IsLoopHeader()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -82,34 +81,36 @@ bool TransformationAddDeadBlock::IsApplicable(
|
|||
}
|
||||
|
||||
void TransformationAddDeadBlock::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
// Update the module id bound so that it is at least the id of the new block.
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
// Get the existing block and its successor.
|
||||
auto existing_block = context->cfg()->block(message_.existing_block());
|
||||
auto existing_block = ir_context->cfg()->block(message_.existing_block());
|
||||
auto successor_block_id =
|
||||
existing_block->terminator()->GetSingleWordInOperand(0);
|
||||
|
||||
// Get the id of the boolean value that will be used as the branch condition.
|
||||
auto bool_id =
|
||||
fuzzerutil::MaybeGetBoolConstantId(context, message_.condition_value());
|
||||
auto bool_id = fuzzerutil::MaybeGetBoolConstantId(ir_context,
|
||||
message_.condition_value());
|
||||
|
||||
// Make a new block that unconditionally branches to the original successor
|
||||
// block.
|
||||
auto enclosing_function = existing_block->GetParent();
|
||||
std::unique_ptr<opt::BasicBlock> new_block = MakeUnique<opt::BasicBlock>(
|
||||
MakeUnique<opt::Instruction>(context, SpvOpLabel, 0, message_.fresh_id(),
|
||||
opt::Instruction::OperandList()));
|
||||
std::unique_ptr<opt::BasicBlock> new_block =
|
||||
MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpLabel, 0, message_.fresh_id(),
|
||||
opt::Instruction::OperandList()));
|
||||
new_block->AddInstruction(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpBranch, 0, 0,
|
||||
ir_context, SpvOpBranch, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
|
||||
|
||||
// Turn the original block into a selection merge, with its original successor
|
||||
// as the merge block.
|
||||
existing_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpSelectionMerge, 0, 0,
|
||||
ir_context, SpvOpSelectionMerge, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {successor_block_id}},
|
||||
{SPV_OPERAND_TYPE_SELECTION_CONTROL,
|
||||
|
@ -135,7 +136,8 @@ void TransformationAddDeadBlock::Apply(
|
|||
existing_block);
|
||||
|
||||
// Record the fact that the new block is dead.
|
||||
fact_manager->AddFactBlockIsDead(message_.fresh_id());
|
||||
transformation_context->GetFactManager()->AddFactBlockIsDead(
|
||||
message_.fresh_id());
|
||||
|
||||
// Fix up OpPhi instructions in the successor block, so that the values they
|
||||
// yield when control has transferred from the new block are the same as if
|
||||
|
@ -143,7 +145,7 @@ void TransformationAddDeadBlock::Apply(
|
|||
// to be valid since |message_.existing_block| dominates the new block by
|
||||
// construction. Other transformations can change these phi operands to more
|
||||
// interesting values.
|
||||
context->cfg()
|
||||
ir_context->cfg()
|
||||
->block(successor_block_id)
|
||||
->ForEachPhiInst([this](opt::Instruction* phi_inst) {
|
||||
// Copy the operand that provides the phi value for the first of any
|
||||
|
@ -156,7 +158,7 @@ void TransformationAddDeadBlock::Apply(
|
|||
|
||||
// Do not rely on any existing analysis results since the control flow graph
|
||||
// of the module has changed.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -41,15 +41,17 @@ class TransformationAddDeadBlock : public Transformation {
|
|||
// - |message_.existing_block| must not be a back-edge block, since in this
|
||||
// case the newly-added block would lead to another back-edge to the
|
||||
// associated loop header
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Changes the OpBranch from |message_.existing_block| to its successor 's'
|
||||
// to an OpBranchConditional to either 's' or a new block,
|
||||
// |message_.fresh_id|, which itself unconditionally branches to 's'. The
|
||||
// conditional branch uses |message.condition_value| as its condition, and is
|
||||
// arranged so that control will pass to 's' at runtime.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
#include "source/fuzz/transformation_add_dead_break.h"
|
||||
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/basic_block.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/struct_cfg_analysis.h"
|
||||
|
@ -39,7 +39,7 @@ TransformationAddDeadBreak::TransformationAddDeadBreak(
|
|||
}
|
||||
|
||||
bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
|
||||
opt::IRContext* context, opt::BasicBlock* bb_from) const {
|
||||
opt::IRContext* ir_context, opt::BasicBlock* bb_from) const {
|
||||
// Look at the structured control flow associated with |from_block| and
|
||||
// check whether it is contained in an appropriate construct with merge id
|
||||
// |to_block| such that a break from |from_block| to |to_block| is legal.
|
||||
|
@ -70,7 +70,7 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
|
|||
// structured control flow construct.
|
||||
|
||||
auto containing_construct =
|
||||
context->GetStructuredCFGAnalysis()->ContainingConstruct(
|
||||
ir_context->GetStructuredCFGAnalysis()->ContainingConstruct(
|
||||
message_.from_block());
|
||||
if (!containing_construct) {
|
||||
// |from_block| is not in a construct from which we can break.
|
||||
|
@ -79,7 +79,7 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
|
|||
|
||||
// Consider case (2)
|
||||
if (message_.to_block() ==
|
||||
context->cfg()->block(containing_construct)->MergeBlockId()) {
|
||||
ir_context->cfg()->block(containing_construct)->MergeBlockId()) {
|
||||
// This looks like an instance of case (2).
|
||||
// However, the structured CFG analysis regards the continue construct of a
|
||||
// loop as part of the loop, but it is not legal to jump from a loop's
|
||||
|
@ -90,28 +90,29 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
|
|||
// currently allow a dead break from a back edge block, but we could and
|
||||
// ultimately should.
|
||||
return !fuzzerutil::BlockIsInLoopContinueConstruct(
|
||||
context, message_.from_block(), containing_construct);
|
||||
ir_context, message_.from_block(), containing_construct);
|
||||
}
|
||||
|
||||
// Case (3) holds if and only if |to_block| is the merge block for this
|
||||
// innermost loop that contains |from_block|
|
||||
auto containing_loop_header =
|
||||
context->GetStructuredCFGAnalysis()->ContainingLoop(
|
||||
ir_context->GetStructuredCFGAnalysis()->ContainingLoop(
|
||||
message_.from_block());
|
||||
if (containing_loop_header &&
|
||||
message_.to_block() ==
|
||||
context->cfg()->block(containing_loop_header)->MergeBlockId()) {
|
||||
ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) {
|
||||
return !fuzzerutil::BlockIsInLoopContinueConstruct(
|
||||
context, message_.from_block(), containing_loop_header);
|
||||
ir_context, message_.from_block(), containing_loop_header);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TransformationAddDeadBreak::IsApplicable(
|
||||
opt::IRContext* context, const FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// First, we check that a constant with the same value as
|
||||
// |message_.break_condition_value| is present.
|
||||
if (!fuzzerutil::MaybeGetBoolConstantId(context,
|
||||
if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
|
||||
message_.break_condition_value())) {
|
||||
// The required constant is not present, so the transformation cannot be
|
||||
// applied.
|
||||
|
@ -121,17 +122,17 @@ bool TransformationAddDeadBreak::IsApplicable(
|
|||
// Check that |message_.from_block| and |message_.to_block| really are block
|
||||
// ids
|
||||
opt::BasicBlock* bb_from =
|
||||
fuzzerutil::MaybeFindBlock(context, message_.from_block());
|
||||
fuzzerutil::MaybeFindBlock(ir_context, message_.from_block());
|
||||
if (bb_from == nullptr) {
|
||||
return false;
|
||||
}
|
||||
opt::BasicBlock* bb_to =
|
||||
fuzzerutil::MaybeFindBlock(context, message_.to_block());
|
||||
fuzzerutil::MaybeFindBlock(ir_context, message_.to_block());
|
||||
if (bb_to == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fuzzerutil::BlockIsReachableInItsFunction(context, bb_to)) {
|
||||
if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) {
|
||||
// If the target of the break is unreachable, we conservatively do not
|
||||
// allow adding a dead break, to avoid the compilations that arise due to
|
||||
// the lack of sensible dominance information for unreachable blocks.
|
||||
|
@ -157,14 +158,14 @@ bool TransformationAddDeadBreak::IsApplicable(
|
|||
"The id of the block we found should match the target id for the break.");
|
||||
|
||||
// Check whether the data passed to extend OpPhi instructions is appropriate.
|
||||
if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from, bb_to,
|
||||
if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, bb_to,
|
||||
message_.phi_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that adding the break would respect the rules of structured
|
||||
// control flow.
|
||||
if (!AddingBreakRespectsStructuredControlFlow(context, bb_from)) {
|
||||
if (!AddingBreakRespectsStructuredControlFlow(ir_context, bb_from)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -177,16 +178,18 @@ bool TransformationAddDeadBreak::IsApplicable(
|
|||
// being places on the validator. This should be revisited if we are sure
|
||||
// the validator is complete with respect to checking structured control flow
|
||||
// rules.
|
||||
auto cloned_context = fuzzerutil::CloneIRContext(context);
|
||||
auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
|
||||
ApplyImpl(cloned_context.get());
|
||||
return fuzzerutil::IsValid(cloned_context.get());
|
||||
return fuzzerutil::IsValid(cloned_context.get(),
|
||||
transformation_context.GetValidatorOptions());
|
||||
}
|
||||
|
||||
void TransformationAddDeadBreak::Apply(opt::IRContext* context,
|
||||
FactManager* /*unused*/) const {
|
||||
ApplyImpl(context);
|
||||
void TransformationAddDeadBreak::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
ApplyImpl(ir_context);
|
||||
// Invalidate all analyses
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddDeadBreak::ToMessage() const {
|
||||
|
@ -196,10 +199,10 @@ protobufs::Transformation TransformationAddDeadBreak::ToMessage() const {
|
|||
}
|
||||
|
||||
void TransformationAddDeadBreak::ApplyImpl(
|
||||
spvtools::opt::IRContext* context) const {
|
||||
spvtools::opt::IRContext* ir_context) const {
|
||||
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
|
||||
context, context->cfg()->block(message_.from_block()),
|
||||
context->cfg()->block(message_.to_block()),
|
||||
ir_context, ir_context->cfg()->block(message_.from_block()),
|
||||
ir_context->cfg()->block(message_.to_block()),
|
||||
message_.break_condition_value(), message_.phi_id());
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -50,21 +50,23 @@ class TransformationAddDeadBreak : public Transformation {
|
|||
// maintain validity of the module.
|
||||
// In particular, the new branch must not lead to violations of the rule
|
||||
// that a use must be dominated by its definition.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Replaces the terminator of a with a conditional branch to b or c.
|
||||
// The boolean constant associated with |message_.break_condition_value| is
|
||||
// used as the condition, and the order of b and c is arranged such that
|
||||
// control is guaranteed to jump to c.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
// Returns true if and only if adding an edge from |bb_from| to
|
||||
// |message_.to_block| respects structured control flow.
|
||||
bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* context,
|
||||
bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* ir_context,
|
||||
opt::BasicBlock* bb_from) const;
|
||||
|
||||
// Used by 'Apply' to actually apply the transformation to the module of
|
||||
|
@ -73,7 +75,7 @@ class TransformationAddDeadBreak : public Transformation {
|
|||
// module. This is only invoked by 'IsApplicable' after certain basic
|
||||
// applicability checks have been made, ensuring that the invocation of this
|
||||
// method is legal.
|
||||
void ApplyImpl(opt::IRContext* context) const;
|
||||
void ApplyImpl(opt::IRContext* ir_context) const;
|
||||
|
||||
protobufs::TransformationAddDeadBreak message_;
|
||||
};
|
||||
|
|
|
@ -34,11 +34,12 @@ TransformationAddDeadContinue::TransformationAddDeadContinue(
|
|||
}
|
||||
|
||||
bool TransformationAddDeadContinue::IsApplicable(
|
||||
opt::IRContext* context, const FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// First, we check that a constant with the same value as
|
||||
// |message_.continue_condition_value| is present.
|
||||
if (!fuzzerutil::MaybeGetBoolConstantId(
|
||||
context, message_.continue_condition_value())) {
|
||||
ir_context, message_.continue_condition_value())) {
|
||||
// The required constant is not present, so the transformation cannot be
|
||||
// applied.
|
||||
return false;
|
||||
|
@ -46,7 +47,7 @@ bool TransformationAddDeadContinue::IsApplicable(
|
|||
|
||||
// Check that |message_.from_block| really is a block id.
|
||||
opt::BasicBlock* bb_from =
|
||||
fuzzerutil::MaybeFindBlock(context, message_.from_block());
|
||||
fuzzerutil::MaybeFindBlock(ir_context, message_.from_block());
|
||||
if (bb_from == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
@ -68,31 +69,33 @@ bool TransformationAddDeadContinue::IsApplicable(
|
|||
// Because the structured CFG analysis does not regard a loop header as part
|
||||
// of the loop it heads, we check first whether bb_from is a loop header
|
||||
// before using the structured CFG analysis.
|
||||
auto loop_header = bb_from->IsLoopHeader()
|
||||
? message_.from_block()
|
||||
: context->GetStructuredCFGAnalysis()->ContainingLoop(
|
||||
message_.from_block());
|
||||
auto loop_header =
|
||||
bb_from->IsLoopHeader()
|
||||
? message_.from_block()
|
||||
: ir_context->GetStructuredCFGAnalysis()->ContainingLoop(
|
||||
message_.from_block());
|
||||
if (!loop_header) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId();
|
||||
auto continue_block =
|
||||
ir_context->cfg()->block(loop_header)->ContinueBlockId();
|
||||
|
||||
if (!fuzzerutil::BlockIsReachableInItsFunction(
|
||||
context, context->cfg()->block(continue_block))) {
|
||||
ir_context, ir_context->cfg()->block(continue_block))) {
|
||||
// If the loop's continue block is unreachable, we conservatively do not
|
||||
// allow adding a dead continue, to avoid the compilations that arise due to
|
||||
// the lack of sensible dominance information for unreachable blocks.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fuzzerutil::BlockIsInLoopContinueConstruct(context, message_.from_block(),
|
||||
loop_header)) {
|
||||
if (fuzzerutil::BlockIsInLoopContinueConstruct(
|
||||
ir_context, message_.from_block(), loop_header)) {
|
||||
// We cannot jump to the continue target from the continue construct.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
|
||||
if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
|
||||
// A branch straight to the continue target that is also a merge block might
|
||||
// break the property that a construct header must dominate its merge block
|
||||
// (if the merge block is reachable).
|
||||
|
@ -100,8 +103,8 @@ bool TransformationAddDeadContinue::IsApplicable(
|
|||
}
|
||||
|
||||
// Check whether the data passed to extend OpPhi instructions is appropriate.
|
||||
if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
|
||||
context->cfg()->block(continue_block),
|
||||
if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from,
|
||||
ir_context->cfg()->block(continue_block),
|
||||
message_.phi_id())) {
|
||||
return false;
|
||||
}
|
||||
|
@ -115,16 +118,18 @@ bool TransformationAddDeadContinue::IsApplicable(
|
|||
// being placed on the validator. This should be revisited if we are sure
|
||||
// the validator is complete with respect to checking structured control flow
|
||||
// rules.
|
||||
auto cloned_context = fuzzerutil::CloneIRContext(context);
|
||||
auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
|
||||
ApplyImpl(cloned_context.get());
|
||||
return fuzzerutil::IsValid(cloned_context.get());
|
||||
return fuzzerutil::IsValid(cloned_context.get(),
|
||||
transformation_context.GetValidatorOptions());
|
||||
}
|
||||
|
||||
void TransformationAddDeadContinue::Apply(opt::IRContext* context,
|
||||
FactManager* /*unused*/) const {
|
||||
ApplyImpl(context);
|
||||
void TransformationAddDeadContinue::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
ApplyImpl(ir_context);
|
||||
// Invalidate all analyses
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
|
||||
|
@ -134,16 +139,16 @@ protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
|
|||
}
|
||||
|
||||
void TransformationAddDeadContinue::ApplyImpl(
|
||||
spvtools::opt::IRContext* context) const {
|
||||
auto bb_from = context->cfg()->block(message_.from_block());
|
||||
spvtools::opt::IRContext* ir_context) const {
|
||||
auto bb_from = ir_context->cfg()->block(message_.from_block());
|
||||
auto continue_block =
|
||||
bb_from->IsLoopHeader()
|
||||
? bb_from->ContinueBlockId()
|
||||
: context->GetStructuredCFGAnalysis()->LoopContinueBlock(
|
||||
: ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock(
|
||||
message_.from_block());
|
||||
assert(continue_block && "message_.from_block must be in a loop.");
|
||||
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
|
||||
context, bb_from, context->cfg()->block(continue_block),
|
||||
ir_context, bb_from, ir_context->cfg()->block(continue_block),
|
||||
message_.continue_condition_value(), message_.phi_id());
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -52,14 +52,16 @@ class TransformationAddDeadContinue : public Transformation {
|
|||
// In particular, adding an edge from somewhere in the loop to the continue
|
||||
// target must not prevent uses of ids in the continue target from being
|
||||
// dominated by the definitions of those ids.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Replaces the terminator of a with a conditional branch to b or c.
|
||||
// The boolean constant associated with |message_.continue_condition_value| is
|
||||
// used as the condition, and the order of b and c is arranged such that
|
||||
// control is guaranteed to jump to c.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
@ -70,7 +72,7 @@ class TransformationAddDeadContinue : public Transformation {
|
|||
// module. This is only invoked by 'IsApplicable' after certain basic
|
||||
// applicability checks have been made, ensuring that the invocation of this
|
||||
// method is legal.
|
||||
void ApplyImpl(opt::IRContext* context) const;
|
||||
void ApplyImpl(opt::IRContext* ir_context) const;
|
||||
|
||||
protobufs::TransformationAddDeadContinue message_;
|
||||
};
|
||||
|
|
|
@ -56,8 +56,8 @@ TransformationAddFunction::TransformationAddFunction(
|
|||
}
|
||||
|
||||
bool TransformationAddFunction::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& fact_manager) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// This transformation may use a lot of ids, all of which need to be fresh
|
||||
// and distinct. This set tracks them.
|
||||
std::set<uint32_t> ids_used_by_this_transformation;
|
||||
|
@ -66,7 +66,7 @@ bool TransformationAddFunction::IsApplicable(
|
|||
for (auto& instruction : message_.instruction()) {
|
||||
if (instruction.result_id()) {
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
instruction.result_id(), context,
|
||||
instruction.result_id(), ir_context,
|
||||
&ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -77,28 +77,28 @@ bool TransformationAddFunction::IsApplicable(
|
|||
// Ensure that all ids provided for making the function livesafe are fresh
|
||||
// and distinct.
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
message_.loop_limiter_variable_id(), context,
|
||||
message_.loop_limiter_variable_id(), ir_context,
|
||||
&ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
for (auto& loop_limiter_info : message_.loop_limiter_info()) {
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
loop_limiter_info.load_id(), context,
|
||||
loop_limiter_info.load_id(), ir_context,
|
||||
&ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
loop_limiter_info.increment_id(), context,
|
||||
loop_limiter_info.increment_id(), ir_context,
|
||||
&ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
loop_limiter_info.compare_id(), context,
|
||||
loop_limiter_info.compare_id(), ir_context,
|
||||
&ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
loop_limiter_info.logical_op_id(), context,
|
||||
loop_limiter_info.logical_op_id(), ir_context,
|
||||
&ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -107,11 +107,11 @@ bool TransformationAddFunction::IsApplicable(
|
|||
message_.access_chain_clamping_info()) {
|
||||
for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) {
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
pair.first(), context, &ids_used_by_this_transformation)) {
|
||||
pair.first(), ir_context, &ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
pair.second(), context, &ids_used_by_this_transformation)) {
|
||||
pair.second(), ir_context, &ids_used_by_this_transformation)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -123,8 +123,8 @@ bool TransformationAddFunction::IsApplicable(
|
|||
// is taken here.
|
||||
|
||||
// We first clone the current module, so that we can try adding the new
|
||||
// function without risking wrecking |context|.
|
||||
auto cloned_module = fuzzerutil::CloneIRContext(context);
|
||||
// function without risking wrecking |ir_context|.
|
||||
auto cloned_module = fuzzerutil::CloneIRContext(ir_context);
|
||||
|
||||
// We try to add a function to the cloned module, which may fail if
|
||||
// |message_.instruction| is not sufficiently well-formed.
|
||||
|
@ -134,12 +134,14 @@ bool TransformationAddFunction::IsApplicable(
|
|||
|
||||
// Check whether the cloned module is still valid after adding the function.
|
||||
// If it is not, the transformation is not applicable.
|
||||
if (!fuzzerutil::IsValid(cloned_module.get())) {
|
||||
if (!fuzzerutil::IsValid(cloned_module.get(),
|
||||
transformation_context.GetValidatorOptions())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message_.is_livesafe()) {
|
||||
if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) {
|
||||
if (!TryToMakeFunctionLivesafe(cloned_module.get(),
|
||||
transformation_context)) {
|
||||
return false;
|
||||
}
|
||||
// After making the function livesafe, we check validity of the module
|
||||
|
@ -148,7 +150,8 @@ bool TransformationAddFunction::IsApplicable(
|
|||
// has the potential to make the module invalid when it was otherwise valid.
|
||||
// It is simpler to rely on the validator to guard against this than to
|
||||
// consider all scenarios when making a function livesafe.
|
||||
if (!fuzzerutil::IsValid(cloned_module.get())) {
|
||||
if (!fuzzerutil::IsValid(cloned_module.get(),
|
||||
transformation_context.GetValidatorOptions())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -156,10 +159,11 @@ bool TransformationAddFunction::IsApplicable(
|
|||
}
|
||||
|
||||
void TransformationAddFunction::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
// Add the function to the module. As the transformation is applicable, this
|
||||
// should succeed.
|
||||
bool success = TryToAddFunction(context);
|
||||
bool success = TryToAddFunction(ir_context);
|
||||
assert(success && "The function should be successfully added.");
|
||||
(void)(success); // Keep release builds happy (otherwise they may complain
|
||||
// that |success| is not used).
|
||||
|
@ -172,16 +176,16 @@ void TransformationAddFunction::Apply(
|
|||
for (auto& instruction : message_.instruction()) {
|
||||
switch (instruction.opcode()) {
|
||||
case SpvOpFunctionParameter:
|
||||
if (context->get_def_use_mgr()
|
||||
if (ir_context->get_def_use_mgr()
|
||||
->GetDef(instruction.result_type_id())
|
||||
->opcode() == SpvOpTypePointer) {
|
||||
fact_manager->AddFactValueOfPointeeIsIrrelevant(
|
||||
instruction.result_id());
|
||||
transformation_context->GetFactManager()
|
||||
->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
|
||||
}
|
||||
break;
|
||||
case SpvOpVariable:
|
||||
fact_manager->AddFactValueOfPointeeIsIrrelevant(
|
||||
instruction.result_id());
|
||||
transformation_context->GetFactManager()
|
||||
->AddFactValueOfPointeeIsIrrelevant(instruction.result_id());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -190,7 +194,7 @@ void TransformationAddFunction::Apply(
|
|||
|
||||
if (message_.is_livesafe()) {
|
||||
// Make the function livesafe, which also should succeed.
|
||||
success = TryToMakeFunctionLivesafe(context, *fact_manager);
|
||||
success = TryToMakeFunctionLivesafe(ir_context, *transformation_context);
|
||||
assert(success && "It should be possible to make the function livesafe.");
|
||||
(void)(success); // Keep release builds happy.
|
||||
|
||||
|
@ -198,17 +202,18 @@ void TransformationAddFunction::Apply(
|
|||
assert(message_.instruction(0).opcode() == SpvOpFunction &&
|
||||
"The first instruction of an 'add function' transformation must be "
|
||||
"OpFunction.");
|
||||
fact_manager->AddFactFunctionIsLivesafe(
|
||||
transformation_context->GetFactManager()->AddFactFunctionIsLivesafe(
|
||||
message_.instruction(0).result_id());
|
||||
} else {
|
||||
// Inform the fact manager that all blocks in the function are dead.
|
||||
for (auto& inst : message_.instruction()) {
|
||||
if (inst.opcode() == SpvOpLabel) {
|
||||
fact_manager->AddFactBlockIsDead(inst.result_id());
|
||||
transformation_context->GetFactManager()->AddFactBlockIsDead(
|
||||
inst.result_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddFunction::ToMessage() const {
|
||||
|
@ -218,9 +223,9 @@ protobufs::Transformation TransformationAddFunction::ToMessage() const {
|
|||
}
|
||||
|
||||
bool TransformationAddFunction::TryToAddFunction(
|
||||
opt::IRContext* context) const {
|
||||
opt::IRContext* ir_context) const {
|
||||
// This function returns false if |message_.instruction| was not well-formed
|
||||
// enough to actually create a function and add it to |context|.
|
||||
// enough to actually create a function and add it to |ir_context|.
|
||||
|
||||
// A function must have at least some instructions.
|
||||
if (message_.instruction().empty()) {
|
||||
|
@ -235,7 +240,7 @@ bool TransformationAddFunction::TryToAddFunction(
|
|||
|
||||
// Make a function, headed by the OpFunction instruction.
|
||||
std::unique_ptr<opt::Function> new_function = MakeUnique<opt::Function>(
|
||||
InstructionFromMessage(context, function_begin));
|
||||
InstructionFromMessage(ir_context, function_begin));
|
||||
|
||||
// Keeps track of which instruction protobuf message we are currently
|
||||
// considering.
|
||||
|
@ -249,7 +254,7 @@ bool TransformationAddFunction::TryToAddFunction(
|
|||
message_.instruction(instruction_index).opcode() ==
|
||||
SpvOpFunctionParameter) {
|
||||
new_function->AddParameter(InstructionFromMessage(
|
||||
context, message_.instruction(instruction_index)));
|
||||
ir_context, message_.instruction(instruction_index)));
|
||||
instruction_index++;
|
||||
}
|
||||
|
||||
|
@ -270,7 +275,7 @@ bool TransformationAddFunction::TryToAddFunction(
|
|||
// as its parent.
|
||||
std::unique_ptr<opt::BasicBlock> block =
|
||||
MakeUnique<opt::BasicBlock>(InstructionFromMessage(
|
||||
context, message_.instruction(instruction_index)));
|
||||
ir_context, message_.instruction(instruction_index)));
|
||||
block->SetParent(new_function.get());
|
||||
|
||||
// Consider successive instructions until we hit another label or the end
|
||||
|
@ -281,7 +286,7 @@ bool TransformationAddFunction::TryToAddFunction(
|
|||
SpvOpFunctionEnd &&
|
||||
message_.instruction(instruction_index).opcode() != SpvOpLabel) {
|
||||
block->AddInstruction(InstructionFromMessage(
|
||||
context, message_.instruction(instruction_index)));
|
||||
ir_context, message_.instruction(instruction_index)));
|
||||
instruction_index++;
|
||||
}
|
||||
// Add the block to the new function.
|
||||
|
@ -295,22 +300,23 @@ bool TransformationAddFunction::TryToAddFunction(
|
|||
}
|
||||
// Set the function's final instruction, add the function to the module and
|
||||
// report success.
|
||||
new_function->SetFunctionEnd(
|
||||
InstructionFromMessage(context, message_.instruction(instruction_index)));
|
||||
context->AddFunction(std::move(new_function));
|
||||
new_function->SetFunctionEnd(InstructionFromMessage(
|
||||
ir_context, message_.instruction(instruction_index)));
|
||||
ir_context->AddFunction(std::move(new_function));
|
||||
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransformationAddFunction::TryToMakeFunctionLivesafe(
|
||||
opt::IRContext* context, const FactManager& fact_manager) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
assert(message_.is_livesafe() && "Precondition: is_livesafe must hold.");
|
||||
|
||||
// Get a pointer to the added function.
|
||||
opt::Function* added_function = nullptr;
|
||||
for (auto& function : *context->module()) {
|
||||
for (auto& function : *ir_context->module()) {
|
||||
if (function.result_id() == message_.instruction(0).result_id()) {
|
||||
added_function = &function;
|
||||
break;
|
||||
|
@ -318,7 +324,7 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe(
|
|||
}
|
||||
assert(added_function && "The added function should have been found.");
|
||||
|
||||
if (!TryToAddLoopLimiters(context, added_function)) {
|
||||
if (!TryToAddLoopLimiters(ir_context, added_function)) {
|
||||
// Adding loop limiters did not work; bail out.
|
||||
return false;
|
||||
}
|
||||
|
@ -332,20 +338,20 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe(
|
|||
switch (inst.opcode()) {
|
||||
case SpvOpKill:
|
||||
case SpvOpUnreachable:
|
||||
if (!TryToTurnKillOrUnreachableIntoReturn(context, added_function,
|
||||
if (!TryToTurnKillOrUnreachableIntoReturn(ir_context, added_function,
|
||||
&inst)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
if (!TryToClampAccessChainIndices(context, &inst)) {
|
||||
if (!TryToClampAccessChainIndices(ir_context, &inst)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SpvOpFunctionCall:
|
||||
// A livesafe function my only call other livesafe functions.
|
||||
if (!fact_manager.FunctionIsLivesafe(
|
||||
if (!transformation_context.GetFactManager()->FunctionIsLivesafe(
|
||||
inst.GetSingleWordInOperand(0))) {
|
||||
return false;
|
||||
}
|
||||
|
@ -358,7 +364,7 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe(
|
|||
}
|
||||
|
||||
bool TransformationAddFunction::TryToAddLoopLimiters(
|
||||
opt::IRContext* context, opt::Function* added_function) const {
|
||||
opt::IRContext* ir_context, opt::Function* added_function) const {
|
||||
// Collect up all the loop headers so that we can subsequently add loop
|
||||
// limiting logic.
|
||||
std::vector<opt::BasicBlock*> loop_headers;
|
||||
|
@ -377,7 +383,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// manipulating a loop limiter.
|
||||
|
||||
auto loop_limit_constant_id_instr =
|
||||
context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
|
||||
if (!loop_limit_constant_id_instr ||
|
||||
loop_limit_constant_id_instr->opcode() != SpvOpConstant) {
|
||||
// The loop limit constant id instruction must exist and have an
|
||||
|
@ -385,7 +391,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
return false;
|
||||
}
|
||||
|
||||
auto loop_limit_type = context->get_def_use_mgr()->GetDef(
|
||||
auto loop_limit_type = ir_context->get_def_use_mgr()->GetDef(
|
||||
loop_limit_constant_id_instr->type_id());
|
||||
if (loop_limit_type->opcode() != SpvOpTypeInt ||
|
||||
loop_limit_type->GetSingleWordInOperand(0) != 32) {
|
||||
|
@ -397,36 +403,36 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// Find the id of the "unsigned int" type.
|
||||
opt::analysis::Integer unsigned_int_type(32, false);
|
||||
uint32_t unsigned_int_type_id =
|
||||
context->get_type_mgr()->GetId(&unsigned_int_type);
|
||||
ir_context->get_type_mgr()->GetId(&unsigned_int_type);
|
||||
if (!unsigned_int_type_id) {
|
||||
// Unsigned int is not available; we need this type in order to add loop
|
||||
// limiters.
|
||||
return false;
|
||||
}
|
||||
auto registered_unsigned_int_type =
|
||||
context->get_type_mgr()->GetRegisteredType(&unsigned_int_type);
|
||||
ir_context->get_type_mgr()->GetRegisteredType(&unsigned_int_type);
|
||||
|
||||
// Look for 0 of type unsigned int.
|
||||
opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(),
|
||||
{0});
|
||||
auto registered_zero = context->get_constant_mgr()->FindConstant(&zero);
|
||||
auto registered_zero = ir_context->get_constant_mgr()->FindConstant(&zero);
|
||||
if (!registered_zero) {
|
||||
// We need 0 in order to be able to initialize loop limiters.
|
||||
return false;
|
||||
}
|
||||
uint32_t zero_id = context->get_constant_mgr()
|
||||
uint32_t zero_id = ir_context->get_constant_mgr()
|
||||
->GetDefiningInstruction(registered_zero)
|
||||
->result_id();
|
||||
|
||||
// Look for 1 of type unsigned int.
|
||||
opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(),
|
||||
{1});
|
||||
auto registered_one = context->get_constant_mgr()->FindConstant(&one);
|
||||
auto registered_one = ir_context->get_constant_mgr()->FindConstant(&one);
|
||||
if (!registered_one) {
|
||||
// We need 1 in order to be able to increment loop limiters.
|
||||
return false;
|
||||
}
|
||||
uint32_t one_id = context->get_constant_mgr()
|
||||
uint32_t one_id = ir_context->get_constant_mgr()
|
||||
->GetDefiningInstruction(registered_one)
|
||||
->result_id();
|
||||
|
||||
|
@ -434,7 +440,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
opt::analysis::Pointer pointer_to_unsigned_int_type(
|
||||
registered_unsigned_int_type, SpvStorageClassFunction);
|
||||
uint32_t pointer_to_unsigned_int_type_id =
|
||||
context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
|
||||
ir_context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
|
||||
if (!pointer_to_unsigned_int_type_id) {
|
||||
// We need pointer-to-unsigned int in order to declare the loop limiter
|
||||
// variable.
|
||||
|
@ -443,7 +449,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
|
||||
// Look for bool type.
|
||||
opt::analysis::Bool bool_type;
|
||||
uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
|
||||
uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type);
|
||||
if (!bool_type_id) {
|
||||
// We need bool in order to compare the loop limiter's value with the loop
|
||||
// limit constant.
|
||||
|
@ -454,22 +460,23 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// block, via an instruction of the form:
|
||||
// %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero
|
||||
added_function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpVariable, pointer_to_unsigned_int_type_id,
|
||||
ir_context, SpvOpVariable, pointer_to_unsigned_int_type_id,
|
||||
message_.loop_limiter_variable_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
|
||||
{SPV_OPERAND_TYPE_ID, {zero_id}}})));
|
||||
// Update the module's id bound since we have added the loop limiter
|
||||
// variable id.
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.loop_limiter_variable_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context,
|
||||
message_.loop_limiter_variable_id());
|
||||
|
||||
// Consider each loop in turn.
|
||||
for (auto loop_header : loop_headers) {
|
||||
// Look for the loop's back-edge block. This is a predecessor of the loop
|
||||
// header that is dominated by the loop header.
|
||||
uint32_t back_edge_block_id = 0;
|
||||
for (auto pred : context->cfg()->preds(loop_header->id())) {
|
||||
if (context->GetDominatorAnalysis(added_function)
|
||||
for (auto pred : ir_context->cfg()->preds(loop_header->id())) {
|
||||
if (ir_context->GetDominatorAnalysis(added_function)
|
||||
->Dominates(loop_header->id(), pred)) {
|
||||
back_edge_block_id = pred;
|
||||
break;
|
||||
|
@ -481,7 +488,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// move on from this loop.
|
||||
continue;
|
||||
}
|
||||
auto back_edge_block = context->cfg()->block(back_edge_block_id);
|
||||
auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
|
||||
|
||||
// Go through the sequence of loop limiter infos and find the one
|
||||
// corresponding to this loop.
|
||||
|
@ -579,14 +586,15 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// Add a load from the loop limiter variable, of the form:
|
||||
// %t1 = OpLoad %uint32 %loop_limiter
|
||||
new_instructions.push_back(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpLoad, unsigned_int_type_id, loop_limiter_info.load_id(),
|
||||
ir_context, SpvOpLoad, unsigned_int_type_id,
|
||||
loop_limiter_info.load_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}})));
|
||||
|
||||
// Increment the loaded value:
|
||||
// %t2 = OpIAdd %uint32 %t1 %one
|
||||
new_instructions.push_back(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpIAdd, unsigned_int_type_id,
|
||||
ir_context, SpvOpIAdd, unsigned_int_type_id,
|
||||
loop_limiter_info.increment_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
|
||||
|
@ -595,7 +603,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// Store the incremented value back to the loop limiter variable:
|
||||
// OpStore %loop_limiter %t2
|
||||
new_instructions.push_back(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpStore, 0, 0,
|
||||
ir_context, SpvOpStore, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}})));
|
||||
|
@ -605,7 +613,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// or
|
||||
// %t3 = OpULessThan %bool %t1 %loop_limit
|
||||
new_instructions.push_back(MakeUnique<opt::Instruction>(
|
||||
context,
|
||||
ir_context,
|
||||
compare_using_greater_than_equal ? SpvOpUGreaterThanEqual
|
||||
: SpvOpULessThan,
|
||||
bool_type_id, loop_limiter_info.compare_id(),
|
||||
|
@ -615,7 +623,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
|
||||
if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
|
||||
new_instructions.push_back(MakeUnique<opt::Instruction>(
|
||||
context,
|
||||
ir_context,
|
||||
compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd,
|
||||
bool_type_id, loop_limiter_info.logical_op_id(),
|
||||
opt::Instruction::OperandList(
|
||||
|
@ -644,8 +652,9 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
// Check that, if the merge block starts with OpPhi instructions, suitable
|
||||
// ids have been provided to give these instructions a value corresponding
|
||||
// to the new incoming edge from the back edge block.
|
||||
auto merge_block = context->cfg()->block(loop_header->MergeBlockId());
|
||||
if (!fuzzerutil::PhiIdsOkForNewEdge(context, back_edge_block, merge_block,
|
||||
auto merge_block = ir_context->cfg()->block(loop_header->MergeBlockId());
|
||||
if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, back_edge_block,
|
||||
merge_block,
|
||||
loop_limiter_info.phi_id())) {
|
||||
return false;
|
||||
}
|
||||
|
@ -681,16 +690,18 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
|||
|
||||
// Update the module's id bound with respect to the various ids that
|
||||
// have been used for loop limiter manipulation.
|
||||
fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.load_id());
|
||||
fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.increment_id());
|
||||
fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.compare_id());
|
||||
fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.logical_op_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.load_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context,
|
||||
loop_limiter_info.increment_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.compare_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context,
|
||||
loop_limiter_info.logical_op_id());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
|
||||
opt::IRContext* context, opt::Function* added_function,
|
||||
opt::IRContext* ir_context, opt::Function* added_function,
|
||||
opt::Instruction* kill_or_unreachable_inst) const {
|
||||
assert((kill_or_unreachable_inst->opcode() == SpvOpKill ||
|
||||
kill_or_unreachable_inst->opcode() == SpvOpUnreachable) &&
|
||||
|
@ -698,7 +709,7 @@ bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
|
|||
|
||||
// Get the function's return type.
|
||||
auto function_return_type_inst =
|
||||
context->get_def_use_mgr()->GetDef(added_function->type_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(added_function->type_id());
|
||||
|
||||
if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
|
||||
// The function has void return type, so change this instruction to
|
||||
|
@ -712,7 +723,7 @@ bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
|
|||
// We first check that the id, %id, provided with the transformation
|
||||
// specifically to turn OpKill and OpUnreachable instructions into
|
||||
// OpReturnValue %id has the same type as the function's return type.
|
||||
if (context->get_def_use_mgr()
|
||||
if (ir_context->get_def_use_mgr()
|
||||
->GetDef(message_.kill_unreachable_return_value_id())
|
||||
->type_id() != function_return_type_inst->result_id()) {
|
||||
return false;
|
||||
|
@ -725,7 +736,7 @@ bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
|
|||
}
|
||||
|
||||
bool TransformationAddFunction::TryToClampAccessChainIndices(
|
||||
opt::IRContext* context, opt::Instruction* access_chain_inst) const {
|
||||
opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const {
|
||||
assert((access_chain_inst->opcode() == SpvOpAccessChain ||
|
||||
access_chain_inst->opcode() == SpvOpInBoundsAccessChain) &&
|
||||
"Precondition: instruction must be OpAccessChain or "
|
||||
|
@ -756,14 +767,14 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
|||
|
||||
// Walk the access chain, clamping each index to be within bounds if it is
|
||||
// not a constant.
|
||||
auto base_object = context->get_def_use_mgr()->GetDef(
|
||||
auto base_object = ir_context->get_def_use_mgr()->GetDef(
|
||||
access_chain_inst->GetSingleWordInOperand(0));
|
||||
assert(base_object && "The base object must exist.");
|
||||
auto pointer_type =
|
||||
context->get_def_use_mgr()->GetDef(base_object->type_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(base_object->type_id());
|
||||
assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer &&
|
||||
"The base object must have pointer type.");
|
||||
auto should_be_composite_type = context->get_def_use_mgr()->GetDef(
|
||||
auto should_be_composite_type = ir_context->get_def_use_mgr()->GetDef(
|
||||
pointer_type->GetSingleWordInOperand(1));
|
||||
|
||||
// Consider each index input operand in turn (operand 0 is the base object).
|
||||
|
@ -784,18 +795,18 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
|||
// Get the bound for the composite being indexed into; e.g. the number of
|
||||
// columns of matrix or the size of an array.
|
||||
uint32_t bound =
|
||||
GetBoundForCompositeIndex(context, *should_be_composite_type);
|
||||
GetBoundForCompositeIndex(ir_context, *should_be_composite_type);
|
||||
|
||||
// Get the instruction associated with the index and figure out its integer
|
||||
// type.
|
||||
const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index);
|
||||
auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
|
||||
auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
auto index_type_inst =
|
||||
context->get_def_use_mgr()->GetDef(index_inst->type_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(index_inst->type_id());
|
||||
assert(index_type_inst->opcode() == SpvOpTypeInt);
|
||||
assert(index_type_inst->GetSingleWordInOperand(0) == 32);
|
||||
opt::analysis::Integer* index_int_type =
|
||||
context->get_type_mgr()
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(index_type_inst->result_id())
|
||||
->AsInteger();
|
||||
|
||||
|
@ -805,20 +816,20 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
|||
"Access chain indices into structures are required to be "
|
||||
"constants.");
|
||||
opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1});
|
||||
if (!context->get_constant_mgr()->FindConstant(&bound_minus_one)) {
|
||||
if (!ir_context->get_constant_mgr()->FindConstant(&bound_minus_one)) {
|
||||
// We do not have an integer constant whose value is |bound| -1.
|
||||
return false;
|
||||
}
|
||||
|
||||
opt::analysis::Bool bool_type;
|
||||
uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
|
||||
uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type);
|
||||
if (!bool_type_id) {
|
||||
// Bool type is not declared; we cannot do a comparison.
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t bound_minus_one_id =
|
||||
context->get_constant_mgr()
|
||||
ir_context->get_constant_mgr()
|
||||
->GetDefiningInstruction(&bound_minus_one)
|
||||
->result_id();
|
||||
|
||||
|
@ -832,7 +843,7 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
|||
// Compare the index with the bound via an instruction of the form:
|
||||
// %t1 = OpULessThanEqual %bool %index %bound_minus_one
|
||||
new_instructions.push_back(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpULessThanEqual, bool_type_id, compare_id,
|
||||
ir_context, SpvOpULessThanEqual, bool_type_id, compare_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
|
||||
|
@ -840,7 +851,7 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
|||
// Select the index if in-bounds, otherwise one less than the bound:
|
||||
// %t2 = OpSelect %int_type %t1 %index %bound_minus_one
|
||||
new_instructions.push_back(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpSelect, index_type_inst->result_id(), select_id,
|
||||
ir_context, SpvOpSelect, index_type_inst->result_id(), select_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {compare_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
|
||||
|
@ -851,8 +862,8 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
|||
|
||||
// Replace %index with %t2.
|
||||
access_chain_inst->SetInOperand(index, {select_id});
|
||||
fuzzerutil::UpdateModuleIdBound(context, compare_id);
|
||||
fuzzerutil::UpdateModuleIdBound(context, select_id);
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, compare_id);
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, select_id);
|
||||
} else {
|
||||
// TODO(afd): At present the SPIR-V spec is not clear on whether
|
||||
// statically out-of-bounds indices mean that a module is invalid (so
|
||||
|
@ -870,16 +881,16 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
|||
}
|
||||
}
|
||||
should_be_composite_type =
|
||||
FollowCompositeIndex(context, *should_be_composite_type, index_id);
|
||||
FollowCompositeIndex(ir_context, *should_be_composite_type, index_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
|
||||
opt::IRContext* context, const opt::Instruction& composite_type_inst) {
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst) {
|
||||
switch (composite_type_inst.opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
return fuzzerutil::GetArraySize(composite_type_inst, context);
|
||||
return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
return composite_type_inst.GetSingleWordInOperand(1);
|
||||
|
@ -893,7 +904,7 @@ uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
|
|||
}
|
||||
|
||||
opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
|
||||
opt::IRContext* context, const opt::Instruction& composite_type_inst,
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
|
||||
uint32_t index_id) {
|
||||
uint32_t sub_object_type_id;
|
||||
switch (composite_type_inst.opcode()) {
|
||||
|
@ -905,12 +916,12 @@ opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
|
|||
sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
|
||||
break;
|
||||
case SpvOpTypeStruct: {
|
||||
auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
|
||||
auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
assert(index_inst->opcode() == SpvOpConstant);
|
||||
assert(
|
||||
context->get_def_use_mgr()->GetDef(index_inst->type_id())->opcode() ==
|
||||
SpvOpTypeInt);
|
||||
assert(context->get_def_use_mgr()
|
||||
assert(ir_context->get_def_use_mgr()
|
||||
->GetDef(index_inst->type_id())
|
||||
->opcode() == SpvOpTypeInt);
|
||||
assert(ir_context->get_def_use_mgr()
|
||||
->GetDef(index_inst->type_id())
|
||||
->GetSingleWordInOperand(0) == 32);
|
||||
uint32_t index_value = index_inst->GetSingleWordInOperand(0);
|
||||
|
@ -924,7 +935,7 @@ opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
|
|||
break;
|
||||
}
|
||||
assert(sub_object_type_id && "No sub-object found.");
|
||||
return context->get_def_use_mgr()->GetDef(sub_object_type_id);
|
||||
return ir_context->get_def_use_mgr()->GetDef(sub_object_type_id);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -47,12 +47,14 @@ class TransformationAddFunction : public Transformation {
|
|||
// ingredients to make the function livesafe, and the function must only
|
||||
// invoke other livesafe functions
|
||||
// - Adding the created function to the module must lead to a valid module.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds the function defined by |message_.instruction| to the module, making
|
||||
// it livesafe if |message_.is_livesafe| holds.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
@ -61,26 +63,26 @@ class TransformationAddFunction : public Transformation {
|
|||
// an array, the number of components of a vector, or the number of columns of
|
||||
// a matrix.
|
||||
static uint32_t GetBoundForCompositeIndex(
|
||||
opt::IRContext* context, const opt::Instruction& composite_type_inst);
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst);
|
||||
|
||||
// Helper method that, given composite type |composite_type_inst|, returns the
|
||||
// type of the sub-object at index |index_id|, which is required to be in-
|
||||
// bounds.
|
||||
static opt::Instruction* FollowCompositeIndex(
|
||||
opt::IRContext* context, const opt::Instruction& composite_type_inst,
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
|
||||
uint32_t index_id);
|
||||
|
||||
private:
|
||||
// Attempts to create a function from the series of instructions in
|
||||
// |message_.instruction| and add it to |context|.
|
||||
// |message_.instruction| and add it to |ir_context|.
|
||||
//
|
||||
// Returns false if adding the function is not possible due to the messages
|
||||
// not respecting the basic structure of a function, e.g. if there is no
|
||||
// OpFunction instruction or no blocks; in this case |context| is left in an
|
||||
// indeterminate state.
|
||||
// OpFunction instruction or no blocks; in this case |ir_context| is left in
|
||||
// an indeterminate state.
|
||||
//
|
||||
// Otherwise returns true. Whether |context| is valid after addition of the
|
||||
// function depends on the contents of |message_.instruction|.
|
||||
// Otherwise returns true. Whether |ir_context| is valid after addition of
|
||||
// the function depends on the contents of |message_.instruction|.
|
||||
//
|
||||
// Intended usage:
|
||||
// - Perform a dry run of this method on a clone of a module, and use
|
||||
|
@ -89,30 +91,31 @@ class TransformationAddFunction : public Transformation {
|
|||
// added, or leads to an invalid module.
|
||||
// - If the dry run succeeds, run the method on the real module of interest,
|
||||
// to add the function.
|
||||
bool TryToAddFunction(opt::IRContext* context) const;
|
||||
bool TryToAddFunction(opt::IRContext* ir_context) const;
|
||||
|
||||
// Should only be called if |message_.is_livesafe| holds. Attempts to make
|
||||
// the function livesafe (see FactFunctionIsLivesafe for a definition).
|
||||
// Returns false if this is not possible, due to |message_| or |context| not
|
||||
// containing sufficient ingredients (such as types and fresh ids) to add
|
||||
// Returns false if this is not possible, due to |message_| or |ir_context|
|
||||
// not containing sufficient ingredients (such as types and fresh ids) to add
|
||||
// the instrumentation necessary to make the function livesafe.
|
||||
bool TryToMakeFunctionLivesafe(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const;
|
||||
bool TryToMakeFunctionLivesafe(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const;
|
||||
|
||||
// A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting
|
||||
// logic.
|
||||
bool TryToAddLoopLimiters(opt::IRContext* context,
|
||||
bool TryToAddLoopLimiters(opt::IRContext* ir_context,
|
||||
opt::Function* added_function) const;
|
||||
|
||||
// A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and
|
||||
// OpUnreachable instructions into return instructions.
|
||||
bool TryToTurnKillOrUnreachableIntoReturn(
|
||||
opt::IRContext* context, opt::Function* added_function,
|
||||
opt::IRContext* ir_context, opt::Function* added_function,
|
||||
opt::Instruction* kill_or_unreachable_inst) const;
|
||||
|
||||
// A helper for TryToMakeFunctionLivesafe that tries to clamp access chain
|
||||
// indices so that they are guaranteed to be in-bounds.
|
||||
bool TryToClampAccessChainIndices(opt::IRContext* context,
|
||||
bool TryToClampAccessChainIndices(opt::IRContext* ir_context,
|
||||
opt::Instruction* access_chain_inst) const;
|
||||
|
||||
protobufs::TransformationAddFunction message_;
|
||||
|
|
|
@ -30,26 +30,26 @@ TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id,
|
|||
}
|
||||
|
||||
bool TransformationAddGlobalUndef::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// A fresh id is required.
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
auto type = context->get_type_mgr()->GetType(message_.type_id());
|
||||
auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
|
||||
// The type must exist, and must not be a function type.
|
||||
return type && !type->AsFunction();
|
||||
}
|
||||
|
||||
void TransformationAddGlobalUndef::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
|
||||
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
|
||||
opt::Instruction::OperandList()));
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const {
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -32,12 +32,14 @@ class TransformationAddGlobalUndef : public Transformation {
|
|||
|
||||
// - |message_.fresh_id| must be fresh
|
||||
// - |message_.type_id| must be the id of a non-function type
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds an OpUndef instruction to the module, with |message_.type_id| as its
|
||||
// type. The instruction has result id |message_.fresh_id|.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
|
|
@ -33,14 +33,13 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable(
|
|||
}
|
||||
|
||||
bool TransformationAddGlobalVariable::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// The result id must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
// The type id must correspond to a type.
|
||||
auto type = context->get_type_mgr()->GetType(message_.type_id());
|
||||
auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
@ -55,7 +54,7 @@ bool TransformationAddGlobalVariable::IsApplicable(
|
|||
}
|
||||
// The initializer id must be the id of a constant. Check this with the
|
||||
// constant manager.
|
||||
auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
|
||||
auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds(
|
||||
{message_.initializer_id()});
|
||||
if (constant_id.empty()) {
|
||||
return false;
|
||||
|
@ -71,7 +70,8 @@ bool TransformationAddGlobalVariable::IsApplicable(
|
|||
}
|
||||
|
||||
void TransformationAddGlobalVariable::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
opt::Instruction::OperandList input_operands;
|
||||
input_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
|
||||
|
@ -79,12 +79,12 @@ void TransformationAddGlobalVariable::Apply(
|
|||
input_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
|
||||
}
|
||||
context->module()->AddGlobalValue(
|
||||
MakeUnique<opt::Instruction>(context, SpvOpVariable, message_.type_id(),
|
||||
message_.fresh_id(), input_operands));
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
|
||||
input_operands));
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(context)) {
|
||||
if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(ir_context)) {
|
||||
// Conservatively add this global to the interface of every entry point in
|
||||
// the module. This means that the global is available for other
|
||||
// transformations to use.
|
||||
|
@ -94,18 +94,20 @@ void TransformationAddGlobalVariable::Apply(
|
|||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
|
||||
// this if a more thorough approach to entry point interfaces is taken.
|
||||
for (auto& entry_point : context->module()->entry_points()) {
|
||||
for (auto& entry_point : ir_context->module()->entry_points()) {
|
||||
entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
|
||||
}
|
||||
}
|
||||
|
||||
if (message_.value_is_irrelevant()) {
|
||||
fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
}
|
||||
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
|
||||
|
@ -116,11 +118,11 @@ protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
|
|||
|
||||
bool TransformationAddGlobalVariable::
|
||||
PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
|
||||
opt::IRContext* context) {
|
||||
opt::IRContext* ir_context) {
|
||||
// TODO(afd): We capture the universal environments for which this requirement
|
||||
// holds. The check should be refined on demand for other target
|
||||
// environments.
|
||||
switch (context->grammar().target_env()) {
|
||||
switch (ir_context->grammar().target_env()) {
|
||||
case SPV_ENV_UNIVERSAL_1_0:
|
||||
case SPV_ENV_UNIVERSAL_1_1:
|
||||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -37,23 +37,25 @@ class TransformationAddGlobalVariable : public Transformation {
|
|||
// class
|
||||
// - |message_.initializer_id| must either be 0 or the id of a constant whose
|
||||
// type is the pointee type of |message_.type_id|
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds a global variable with Private storage class to the module, with type
|
||||
// |message_.type_id| and either no initializer or |message_.initializer_id|
|
||||
// as an initializer, depending on whether |message_.initializer_id| is 0.
|
||||
// The global variable has result id |message_.fresh_id|.
|
||||
//
|
||||
// If |message_.value_is_irrelevant| holds, adds a corresponding fact to
|
||||
// |fact_manager|.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
// If |message_.value_is_irrelevant| holds, adds a corresponding fact to the
|
||||
// fact manager in |transformation_context|.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
|
||||
opt::IRContext* context);
|
||||
opt::IRContext* ir_context);
|
||||
|
||||
protobufs::TransformationAddGlobalVariable message_;
|
||||
};
|
||||
|
|
|
@ -34,23 +34,22 @@ TransformationAddLocalVariable::TransformationAddLocalVariable(
|
|||
}
|
||||
|
||||
bool TransformationAddLocalVariable::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// The provided id must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
// The pointer type id must indeed correspond to a pointer, and it must have
|
||||
// function storage class.
|
||||
auto type_instruction =
|
||||
context->get_def_use_mgr()->GetDef(message_.type_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.type_id());
|
||||
if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer ||
|
||||
type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) {
|
||||
return false;
|
||||
}
|
||||
// The initializer must...
|
||||
auto initializer_instruction =
|
||||
context->get_def_use_mgr()->GetDef(message_.initializer_id());
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
|
||||
// ... exist, ...
|
||||
if (!initializer_instruction) {
|
||||
return false;
|
||||
|
@ -65,17 +64,18 @@ bool TransformationAddLocalVariable::IsApplicable(
|
|||
return false;
|
||||
}
|
||||
// The function to which the local variable is to be added must exist.
|
||||
return fuzzerutil::FindFunction(context, message_.function_id());
|
||||
return fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
}
|
||||
|
||||
void TransformationAddLocalVariable::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
fuzzerutil::FindFunction(context, message_.function_id())
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
fuzzerutil::FindFunction(ir_context, message_.function_id())
|
||||
->begin()
|
||||
->begin()
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
|
||||
ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
{
|
||||
|
@ -83,9 +83,10 @@ void TransformationAddLocalVariable::Apply(
|
|||
SpvStorageClassFunction}},
|
||||
{SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
|
||||
if (message_.value_is_irrelevant()) {
|
||||
fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
}
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddLocalVariable::ToMessage() const {
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
|
||||
|
||||
#include "source/fuzz/fact_manager.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 {
|
||||
|
@ -38,15 +38,17 @@ class TransformationAddLocalVariable : public Transformation {
|
|||
// - |message_.initializer_id| must be the id of a constant with the same
|
||||
// type as the pointer's pointee type
|
||||
// - |message_.function_id| must be the id of a function
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds an instruction to the start of |message_.function_id|, of the form:
|
||||
// |message_.fresh_id| = OpVariable |message_.type_id| Function
|
||||
// |message_.initializer_id|
|
||||
// If |message_.value_is_irrelevant| holds, adds a corresponding fact to
|
||||
// |fact_manager|.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
// If |message_.value_is_irrelevant| holds, adds a corresponding fact to the
|
||||
// fact manager in |transformation_context|.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
|
|
|
@ -31,10 +31,9 @@ TransformationAddNoContractionDecoration::
|
|||
}
|
||||
|
||||
bool TransformationAddNoContractionDecoration::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// |message_.result_id| must be the id of an instruction.
|
||||
auto instr = context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
if (!instr) {
|
||||
return false;
|
||||
}
|
||||
|
@ -43,10 +42,10 @@ bool TransformationAddNoContractionDecoration::IsApplicable(
|
|||
}
|
||||
|
||||
void TransformationAddNoContractionDecoration::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// Add a NoContraction decoration targeting |message_.result_id|.
|
||||
context->get_decoration_mgr()->AddDecoration(message_.result_id(),
|
||||
SpvDecorationNoContraction);
|
||||
ir_context->get_decoration_mgr()->AddDecoration(message_.result_id(),
|
||||
SpvDecorationNoContraction);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue