Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2020-04-04 18:53:14 -07:00
parent 9522e65f0f
commit 6a0b5b5b5c
168 changed files with 2128 additions and 1517 deletions

View File

@ -1 +1 @@
"v2020.3-dev", "SPIRV-Tools v2020.3-dev eff6f130119f3e6acbf81f8432c0912296d4dfdb"
"v2020.3-dev", "SPIRV-Tools v2020.3-dev 9522e65f0f430a45a29053e2aeec768c2dbca075"

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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)) {

View File

@ -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;

View File

@ -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(

View File

@ -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_;
};

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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));
}
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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, {});

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.");

View File

@ -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;

View File

@ -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,
&current_best_binary, &current_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) {

View File

@ -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;

View File

@ -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) {

View File

@ -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);
};

View File

@ -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};

View File

@ -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_;

View File

@ -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 {

View File

@ -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;

View File

@ -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()

View File

@ -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;

View File

@ -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

View File

@ -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_

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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());
}

View File

@ -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_;
};

View File

@ -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());
}

View File

@ -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_;
};

View File

@ -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

View File

@ -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_;

View File

@ -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 {

View File

@ -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;

View File

@ -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:

View File

@ -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_;
};

View File

@ -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 {

View File

@ -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;

View File

@ -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