mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2025-01-13 18:00:05 +00:00
spirv-fuzz: Remove opaque pointer design pattern (#3755)
There's no real need for Fuzzer, Replayer and Shrinker to use the opaque pointer design pattern. This change removes it, paving the way for making some upcoming changes to Fuzzer easier.
This commit is contained in:
parent
08291a3a9e
commit
f428aa39ca
@ -16,7 +16,6 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "source/fuzz/fact_manager/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
@ -112,51 +111,32 @@ void MaybeAddPass(
|
||||
|
||||
} // namespace
|
||||
|
||||
struct Fuzzer::Impl {
|
||||
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),
|
||||
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.
|
||||
spv_validator_options validator_options; // Options to control validation.
|
||||
};
|
||||
|
||||
Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
|
||||
Fuzzer::Fuzzer(spv_target_env target_env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options)
|
||||
: impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass,
|
||||
validator_options)) {}
|
||||
: target_env_(target_env),
|
||||
seed_(seed),
|
||||
validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass),
|
||||
validator_options_(validator_options) {}
|
||||
|
||||
Fuzzer::~Fuzzer() = default;
|
||||
|
||||
void Fuzzer::SetMessageConsumer(MessageConsumer c) {
|
||||
impl_->consumer = std::move(c);
|
||||
void Fuzzer::SetMessageConsumer(MessageConsumer consumer) {
|
||||
consumer_ = std::move(consumer);
|
||||
}
|
||||
|
||||
bool Fuzzer::Impl::ApplyPassAndCheckValidity(
|
||||
bool Fuzzer::ApplyPassAndCheckValidity(
|
||||
FuzzerPass* pass, const opt::IRContext& ir_context,
|
||||
const spvtools::SpirvTools& tools) const {
|
||||
pass->Apply();
|
||||
if (validate_after_each_fuzzer_pass) {
|
||||
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(),
|
||||
validator_options)) {
|
||||
consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during fuzzing (set a breakpoint to "
|
||||
"inspect); stopping.");
|
||||
validator_options_)) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during fuzzing (set a breakpoint to "
|
||||
"inspect); stopping.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -173,29 +153,28 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
// header files being used.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
spvtools::SpirvTools tools(impl_->target_env);
|
||||
tools.SetMessageConsumer(impl_->consumer);
|
||||
spvtools::SpirvTools tools(target_env_);
|
||||
tools.SetMessageConsumer(consumer_);
|
||||
if (!tools.IsValid()) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
return Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface;
|
||||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(),
|
||||
impl_->validator_options)) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options_)) {
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid;
|
||||
}
|
||||
|
||||
// Build the module from the input binary.
|
||||
std::unique_ptr<opt::IRContext> ir_context = BuildModule(
|
||||
impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
|
||||
std::unique_ptr<opt::IRContext> ir_context =
|
||||
BuildModule(target_env_, consumer_, binary_in.data(), binary_in.size());
|
||||
assert(ir_context);
|
||||
|
||||
// Make a PRNG from the seed passed to the fuzzer on creation.
|
||||
PseudoRandomGenerator random_generator(impl_->seed);
|
||||
PseudoRandomGenerator random_generator(seed_);
|
||||
|
||||
// The fuzzer will introduce new ids into the module. The module's id bound
|
||||
// gives the smallest id that can be used for this purpose. We add an offset
|
||||
@ -208,9 +187,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
FuzzerContext fuzzer_context(&random_generator, minimum_fresh_id);
|
||||
|
||||
FactManager fact_manager;
|
||||
fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
|
||||
fact_manager.AddFacts(consumer_, initial_facts, ir_context.get());
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
impl_->validator_options);
|
||||
validator_options_);
|
||||
|
||||
// Apply some semantics-preserving passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> passes;
|
||||
@ -356,7 +335,7 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
(is_first ||
|
||||
fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
|
||||
is_first = false;
|
||||
if (!impl_->ApplyPassAndCheckValidity(
|
||||
if (!ApplyPassAndCheckValidity(
|
||||
passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
|
||||
tools)) {
|
||||
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
|
||||
@ -400,7 +379,7 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
&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)) {
|
||||
if (!ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
|
||||
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
@ -37,11 +38,11 @@ class Fuzzer {
|
||||
kInitialBinaryInvalid,
|
||||
};
|
||||
|
||||
// Constructs a fuzzer from the given target environment |env|. |seed| is a
|
||||
// seed for pseudo-random number generation.
|
||||
// Constructs a fuzzer from the given target environment |target_env|. |seed|
|
||||
// is a seed for pseudo-random number generation.
|
||||
// |validate_after_each_fuzzer_pass| controls whether the validator will be
|
||||
// invoked after every fuzzer pass is applied.
|
||||
Fuzzer(spv_target_env env, uint32_t seed,
|
||||
Fuzzer(spv_target_env target_env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options);
|
||||
|
||||
@ -71,8 +72,27 @@ class Fuzzer {
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const;
|
||||
|
||||
private:
|
||||
struct Impl; // Opaque struct for holding internal data.
|
||||
std::unique_ptr<Impl> impl_; // Unique pointer to internal data.
|
||||
// Applies |pass|, which must be a pass constructed with |ir_context|, and
|
||||
// then returns true if and only if |ir_context| is valid. |tools| is used to
|
||||
// check validity.
|
||||
bool ApplyPassAndCheckValidity(FuzzerPass* pass,
|
||||
const opt::IRContext& ir_context,
|
||||
const spvtools::SpirvTools& tools) const;
|
||||
|
||||
// Target environment.
|
||||
const spv_target_env target_env_;
|
||||
|
||||
// Message consumer.
|
||||
MessageConsumer consumer_;
|
||||
|
||||
// Seed for random number generator.
|
||||
const uint32_t seed_;
|
||||
|
||||
// Determines whether the validator should be invoked after every fuzzer pass.
|
||||
bool validate_after_each_fuzzer_pass_;
|
||||
|
||||
// Options to control validation.
|
||||
spv_validator_options validator_options_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -28,28 +28,16 @@
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
struct Replayer::Impl {
|
||||
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,
|
||||
Replayer::Replayer(spv_target_env target_env, bool validate_during_replay,
|
||||
spv_validator_options validator_options)
|
||||
: impl_(MakeUnique<Impl>(env, validate_during_replay, validator_options)) {}
|
||||
: target_env_(target_env),
|
||||
validate_during_replay_(validate_during_replay),
|
||||
validator_options_(validator_options) {}
|
||||
|
||||
Replayer::~Replayer() = default;
|
||||
|
||||
void Replayer::SetMessageConsumer(MessageConsumer c) {
|
||||
impl_->consumer = std::move(c);
|
||||
void Replayer::SetMessageConsumer(MessageConsumer consumer) {
|
||||
consumer_ = std::move(consumer);
|
||||
}
|
||||
|
||||
Replayer::ReplayerResultStatus Replayer::Run(
|
||||
@ -65,47 +53,45 @@ Replayer::ReplayerResultStatus Replayer::Run(
|
||||
|
||||
if (num_transformations_to_apply >
|
||||
static_cast<uint32_t>(transformation_sequence_in.transformation_size())) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"The number of transformations to be replayed must not "
|
||||
"exceed the size of the transformation sequence.");
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"The number of transformations to be replayed must not "
|
||||
"exceed the size of the transformation sequence.");
|
||||
return Replayer::ReplayerResultStatus::kTooManyTransformationsRequested;
|
||||
}
|
||||
|
||||
spvtools::SpirvTools tools(impl_->target_env);
|
||||
spvtools::SpirvTools tools(target_env_);
|
||||
if (!tools.IsValid()) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
return Replayer::ReplayerResultStatus::kFailedToCreateSpirvToolsInterface;
|
||||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(),
|
||||
impl_->validator_options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options_)) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return Replayer::ReplayerResultStatus::kInitialBinaryInvalid;
|
||||
}
|
||||
|
||||
// Build the module from the input binary.
|
||||
std::unique_ptr<opt::IRContext> ir_context = BuildModule(
|
||||
impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
|
||||
std::unique_ptr<opt::IRContext> ir_context =
|
||||
BuildModule(target_env_, consumer_, binary_in.data(), binary_in.size());
|
||||
assert(ir_context);
|
||||
|
||||
// For replay validation, we track the last valid SPIR-V binary that was
|
||||
// observed. Initially this is the input binary.
|
||||
std::vector<uint32_t> last_valid_binary;
|
||||
if (impl_->validate_during_replay) {
|
||||
if (validate_during_replay_) {
|
||||
last_valid_binary = binary_in;
|
||||
}
|
||||
|
||||
FactManager fact_manager;
|
||||
fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
|
||||
fact_manager.AddFacts(consumer_, initial_facts, ir_context.get());
|
||||
std::unique_ptr<TransformationContext> transformation_context =
|
||||
first_overflow_id == 0
|
||||
? MakeUnique<TransformationContext>(&fact_manager,
|
||||
impl_->validator_options)
|
||||
? MakeUnique<TransformationContext>(&fact_manager, validator_options_)
|
||||
: MakeUnique<TransformationContext>(
|
||||
&fact_manager, impl_->validator_options,
|
||||
&fact_manager, validator_options_,
|
||||
MakeUnique<CounterOverflowIdSource>(first_overflow_id));
|
||||
|
||||
// We track the largest id bound observed, to ensure that it only increases
|
||||
@ -136,16 +122,16 @@ Replayer::ReplayerResultStatus Replayer::Run(
|
||||
"transformations.");
|
||||
max_observed_id_bound = ir_context->module()->id_bound();
|
||||
|
||||
if (impl_->validate_during_replay) {
|
||||
if (validate_during_replay_) {
|
||||
std::vector<uint32_t> binary_to_validate;
|
||||
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(),
|
||||
impl_->validator_options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during replay (set a "
|
||||
"breakpoint to inspect); stopping.");
|
||||
validator_options_)) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during replay (set a "
|
||||
"breakpoint to inspect); stopping.");
|
||||
return Replayer::ReplayerResultStatus::kReplayValidationFailure;
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ class Replayer {
|
||||
};
|
||||
|
||||
// Constructs a replayer from the given target environment.
|
||||
Replayer(spv_target_env env, bool validate_during_replay,
|
||||
Replayer(spv_target_env target_env, bool validate_during_replay,
|
||||
spv_validator_options validator_options);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
@ -76,8 +76,17 @@ class Replayer {
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const;
|
||||
|
||||
private:
|
||||
struct Impl; // Opaque struct for holding internal data.
|
||||
std::unique_ptr<Impl> impl_; // Unique pointer to internal data.
|
||||
// Target environment.
|
||||
const spv_target_env target_env_;
|
||||
|
||||
// Message consumer.
|
||||
MessageConsumer consumer_;
|
||||
|
||||
// Controls whether the validator should be run after every replay step.
|
||||
const bool validate_during_replay_;
|
||||
|
||||
// Options to control validation
|
||||
spv_validator_options validator_options_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -61,45 +61,26 @@ protobufs::TransformationSequence RemoveChunk(
|
||||
|
||||
} // namespace
|
||||
|
||||
struct Shrinker::Impl {
|
||||
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) {}
|
||||
|
||||
// Returns the id bound for the given SPIR-V binary, which is assumed to be
|
||||
// valid.
|
||||
uint32_t GetIdBound(const std::vector<uint32_t>& binary);
|
||||
|
||||
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.
|
||||
};
|
||||
|
||||
uint32_t Shrinker::Impl::GetIdBound(const std::vector<uint32_t>& binary) {
|
||||
uint32_t Shrinker::GetIdBound(const std::vector<uint32_t>& binary) const {
|
||||
// Build the module from the input binary.
|
||||
std::unique_ptr<opt::IRContext> ir_context =
|
||||
BuildModule(target_env, consumer, binary.data(), binary.size());
|
||||
BuildModule(target_env_, consumer_, binary.data(), binary.size());
|
||||
assert(ir_context && "Error building module.");
|
||||
return ir_context->module()->id_bound();
|
||||
}
|
||||
|
||||
Shrinker::Shrinker(spv_target_env env, uint32_t step_limit,
|
||||
Shrinker::Shrinker(spv_target_env target_env, uint32_t step_limit,
|
||||
bool validate_during_replay,
|
||||
spv_validator_options validator_options)
|
||||
: impl_(MakeUnique<Impl>(env, step_limit, validate_during_replay,
|
||||
validator_options)) {}
|
||||
: target_env_(target_env),
|
||||
step_limit_(step_limit),
|
||||
validate_during_replay_(validate_during_replay),
|
||||
validator_options_(validator_options) {}
|
||||
|
||||
Shrinker::~Shrinker() = default;
|
||||
|
||||
void Shrinker::SetMessageConsumer(MessageConsumer c) {
|
||||
impl_->consumer = std::move(c);
|
||||
void Shrinker::SetMessageConsumer(MessageConsumer consumer) {
|
||||
consumer_ = std::move(consumer);
|
||||
}
|
||||
|
||||
Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
@ -113,18 +94,17 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
// header files being used.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
SpirvTools tools(impl_->target_env);
|
||||
SpirvTools tools(target_env_);
|
||||
if (!tools.IsValid()) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
return Shrinker::ShrinkerResultStatus::kFailedToCreateSpirvToolsInterface;
|
||||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(),
|
||||
impl_->validator_options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options_)) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return Shrinker::ShrinkerResultStatus::kInitialBinaryInvalid;
|
||||
}
|
||||
|
||||
@ -135,9 +115,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).
|
||||
Replayer replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options);
|
||||
replayer.SetMessageConsumer(impl_->consumer);
|
||||
Replayer replayer(target_env_, validate_during_replay_, validator_options_);
|
||||
replayer.SetMessageConsumer(consumer_);
|
||||
if (replayer.Run(binary_in, initial_facts, transformation_sequence_in,
|
||||
static_cast<uint32_t>(
|
||||
transformation_sequence_in.transformation_size()),
|
||||
@ -150,15 +129,15 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
// Check that the binary produced by applying the initial transformations is
|
||||
// indeed interesting.
|
||||
if (!interestingness_function(current_best_binary, 0)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is not interesting; stopping.");
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is not interesting; stopping.");
|
||||
return ShrinkerResultStatus::kInitialBinaryNotInteresting;
|
||||
}
|
||||
|
||||
// The largest id used by the module before any shrinking has been applied
|
||||
// serves as the first id that can be used for overflow purposes.
|
||||
const uint32_t first_overflow_id = impl_->GetIdBound(current_best_binary);
|
||||
assert(first_overflow_id >= impl_->GetIdBound(binary_in) &&
|
||||
const uint32_t first_overflow_id = GetIdBound(current_best_binary);
|
||||
assert(first_overflow_id >= GetIdBound(binary_in) &&
|
||||
"Applying transformations should only increase a module's id bound.");
|
||||
|
||||
uint32_t attempt = 0; // Keeps track of the number of shrink attempts that
|
||||
@ -174,7 +153,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
// - reach the step limit,
|
||||
// - run out of transformations to remove, or
|
||||
// - cannot make the chunk size any smaller.
|
||||
while (attempt < impl_->step_limit &&
|
||||
while (attempt < step_limit_ &&
|
||||
!current_best_transformations.transformation().empty() &&
|
||||
chunk_size > 0) {
|
||||
bool progress_this_round =
|
||||
@ -204,7 +183,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
// |chunk_size|, using |chunk_index| to track which chunk to try removing
|
||||
// next. The loop exits early if we reach the shrinking step limit.
|
||||
for (int chunk_index = num_chunks - 1;
|
||||
attempt < impl_->step_limit && chunk_index >= 0; chunk_index--) {
|
||||
attempt < step_limit_ && chunk_index >= 0; chunk_index--) {
|
||||
// Remove a chunk of transformations according to the current index and
|
||||
// chunk size.
|
||||
auto transformations_with_chunk_removed =
|
||||
@ -265,12 +244,12 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
|
||||
// Indicate whether shrinking completed or was truncated due to reaching the
|
||||
// step limit.
|
||||
assert(attempt <= impl_->step_limit);
|
||||
if (attempt == impl_->step_limit) {
|
||||
assert(attempt <= step_limit_);
|
||||
if (attempt == step_limit_) {
|
||||
std::stringstream strstream;
|
||||
strstream << "Shrinking did not complete; step limit " << impl_->step_limit
|
||||
strstream << "Shrinking did not complete; step limit " << step_limit_
|
||||
<< " was reached.";
|
||||
impl_->consumer(SPV_MSG_WARNING, nullptr, {}, strstream.str().c_str());
|
||||
consumer_(SPV_MSG_WARNING, nullptr, {}, strstream.str().c_str());
|
||||
return Shrinker::ShrinkerResultStatus::kStepLimitReached;
|
||||
}
|
||||
return Shrinker::ShrinkerResultStatus::kComplete;
|
||||
|
@ -49,8 +49,8 @@ class Shrinker {
|
||||
using InterestingnessFunction = std::function<bool(
|
||||
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 target_env, uint32_t step_limit,
|
||||
bool validate_during_replay,
|
||||
spv_validator_options validator_options);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
@ -82,8 +82,25 @@ class Shrinker {
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const;
|
||||
|
||||
private:
|
||||
struct Impl; // Opaque struct for holding internal data.
|
||||
std::unique_ptr<Impl> impl_; // Unique pointer to internal data.
|
||||
// Returns the id bound for the given SPIR-V binary, which is assumed to be
|
||||
// valid.
|
||||
uint32_t GetIdBound(const std::vector<uint32_t>& binary) const;
|
||||
|
||||
// Target environment.
|
||||
const spv_target_env target_env_;
|
||||
|
||||
// Message consumer.
|
||||
MessageConsumer consumer_;
|
||||
|
||||
// Step limit to decide when to terminate shrinking early.
|
||||
const uint32_t step_limit_;
|
||||
|
||||
// Determines whether to check for validity during the replaying of
|
||||
// transformations.
|
||||
const bool validate_during_replay_;
|
||||
|
||||
// Options to control validation.
|
||||
spv_validator_options validator_options_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
Loading…
Reference in New Issue
Block a user