This change adds the notion of "overflow ids", which can be used
during shrinking to facilitate applying transformations that would
otherwise have become inapplicable due to earlier transformations
being removed.
This commit is contained in:
Alastair Donaldson 2020-08-26 07:49:42 +01:00 коммит произвёл GitHub
Родитель 2205254cfb
Коммит 9e26ae0455
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 669 добавлений и 152 удалений

Просмотреть файл

@ -30,6 +30,7 @@ if(SPIRV_BUILD_FUZZER)
set(SPIRV_TOOLS_FUZZ_SOURCES
call_graph.h
counter_overflow_id_source.h
data_descriptor.h
equivalence_relation.h
fact_manager/constant_uniform_facts.h
@ -100,6 +101,7 @@ if(SPIRV_BUILD_FUZZER)
id_use_descriptor.h
instruction_descriptor.h
instruction_message.h
overflow_id_source.h
protobufs/spirvfuzz_protobufs.h
pseudo_random_generator.h
random_generator.h
@ -180,6 +182,7 @@ if(SPIRV_BUILD_FUZZER)
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
call_graph.cpp
counter_overflow_id_source.cpp
data_descriptor.cpp
fact_manager/constant_uniform_facts.cpp
fact_manager/data_synonym_and_id_equation_facts.cpp
@ -249,6 +252,7 @@ if(SPIRV_BUILD_FUZZER)
id_use_descriptor.cpp
instruction_descriptor.cpp
instruction_message.cpp
overflow_id_source.cpp
pseudo_random_generator.cpp
random_generator.cpp
replayer.cpp

Просмотреть файл

@ -0,0 +1,30 @@
// 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/counter_overflow_id_source.h"
namespace spvtools {
namespace fuzz {
CounterOverflowIdSource::CounterOverflowIdSource(uint32_t first_available_id)
: next_available_id_(first_available_id) {}
bool CounterOverflowIdSource::HasOverflowIds() const { return true; }
uint32_t CounterOverflowIdSource::GetNextOverflowId() {
return next_available_id_++;
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,45 @@
// 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_COUNTER_OVERFLOW_ID_SOURCE_H_
#define SOURCE_FUZZ_COUNTER_OVERFLOW_ID_SOURCE_H_
#include "source/fuzz/overflow_id_source.h"
namespace spvtools {
namespace fuzz {
// A source of overflow ids that uses a counter to provide successive ids from
// a given starting value.
class CounterOverflowIdSource : public OverflowIdSource {
public:
// |first_available_id| is the starting value for the counter.
explicit CounterOverflowIdSource(uint32_t first_available_id);
// Always returns true.
bool HasOverflowIds() const override;
// Returns the current counter value and increments the counter.
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) We should
// account for the case where the maximum allowed id is reached.
uint32_t GetNextOverflowId() override;
private:
uint32_t next_available_id_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_COUNTER_H_

Просмотреть файл

@ -0,0 +1,23 @@
// 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/overflow_id_source.h"
namespace spvtools {
namespace fuzz {
OverflowIdSource::~OverflowIdSource() = default;
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,106 @@
// 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_OVERFLOW_ID_SOURCE_H_
#define SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_
#include <cstdint>
namespace spvtools {
namespace fuzz {
// An implementation of this interface can be used to provide fresh ids on
// demand when applying a transformation.
//
// During fuzzing this should never be required: a fuzzer pass should determine
// all the fresh ids it requires to apply a transformation.
//
// However, during shrinking we can have the situation where, after removing
// an early transformation, a later transformation needs more ids.
//
// As an example, suppose a SPIR-V function originally has this form:
//
// main() {
// stmt1;
// stmt2;
// stmt3;
// stmt4;
// }
//
// Now suppose two *outlining* transformations are applied. The first
// transformation, T1, outlines "stmt1; stmt2;" into a function foo, giving us:
//
// foo() {
// stmt1;
// stmt2;
// }
//
// main() {
// foo();
// stmt3;
// stmt4;
// }
//
// The second transformation, T2, outlines "foo(); stmt3;" from main into a
// function bar, giving us:
//
// foo() {
// stmt1;
// stmt2;
// }
//
// bar() {
// foo();
// stmt3;
// }
//
// main() {
// bar();
// stmt4;
// }
//
// Suppose that T2 used a set of fresh ids, FRESH, in order to perform its
// outlining.
//
// Now suppose that during shrinking we remove T1, but still want to apply T2.
// The fresh ids used by T2 - FRESH - are sufficient to outline "foo(); stmt3;".
// However, because we did not apply T1, "foo();" does not exist and instead the
// task of T2 is to outline "stmt1; stmt2; stmt3;". The set FRESH contains
// *some* of the fresh ids required to do this (those for "stmt3;"), but not all
// of them (those for "stmt1; stmt2;" are missing).
//
// A source of overflow ids can be used to allow the shrinker to proceed
// nevertheless.
//
// It is desirable to use overflow ids only when needed. In our worked example,
// T2 should still use the ids from FRESH when handling "stmt3;", because later
// transformations might refer to those ids and will become inapplicable if
// overflow ids are used instead.
class OverflowIdSource {
public:
virtual ~OverflowIdSource();
// Returns true if and only if this source is capable of providing overflow
// ids.
virtual bool HasOverflowIds() const = 0;
// Precondition: HasOverflowIds() must hold. Returns the next available
// overflow id.
virtual uint32_t GetNextOverflowId() = 0;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_

Просмотреть файл

@ -14,8 +14,10 @@
#include "source/fuzz/replayer.h"
#include <memory>
#include <utility>
#include "source/fuzz/counter_overflow_id_source.h"
#include "source/fuzz/fact_manager/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
@ -54,7 +56,8 @@ Replayer::ReplayerResultStatus Replayer::Run(
const std::vector<uint32_t>& binary_in,
const protobufs::FactSequence& initial_facts,
const protobufs::TransformationSequence& transformation_sequence_in,
uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
uint32_t num_transformations_to_apply, uint32_t first_overflow_id,
std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const {
// Check compatibility between the library version being linked with and the
// header files being used.
@ -97,8 +100,18 @@ 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);
std::unique_ptr<TransformationContext> transformation_context =
first_overflow_id == 0
? MakeUnique<TransformationContext>(&fact_manager,
impl_->validator_options)
: MakeUnique<TransformationContext>(
&fact_manager, impl_->validator_options,
MakeUnique<CounterOverflowIdSource>(first_overflow_id));
// We track the largest id bound observed, to ensure that it only increases
// as transformations are applied.
uint32_t max_observed_id_bound = ir_context->module()->id_bound();
(void)(max_observed_id_bound); // Keep release-mode compilers happy.
// Consider the transformation proto messages in turn.
uint32_t counter = 0;
@ -112,12 +125,17 @@ Replayer::ReplayerResultStatus Replayer::Run(
// Check whether the transformation can be applied.
if (transformation->IsApplicable(ir_context.get(),
transformation_context)) {
*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(), &transformation_context);
transformation->Apply(ir_context.get(), transformation_context.get());
*transformation_sequence_out->add_transformation() = message;
assert(ir_context->module()->id_bound() >= max_observed_id_bound &&
"The module's id bound should only increase due to applying "
"transformations.");
max_observed_id_bound = ir_context->module()->id_bound();
if (impl_->validate_during_replay) {
std::vector<uint32_t> binary_to_validate;
ir_context->module()->ToBinary(&binary_to_validate, false);

Просмотреть файл

@ -55,15 +55,24 @@ class Replayer {
// Transforms |binary_in| to |binary_out| by attempting to apply the first
// |num_transformations_to_apply| transformations from
// |transformation_sequence_in|. Initial facts about the input binary and the
// context in which it will execute are provided via |initial_facts|. The
// transformations that were successfully applied are returned via
// |transformation_sequence_in|.
//
// Initial facts about the input binary and the context in which it will
// execute are provided via |initial_facts|.
//
// |first_overflow_id| should be set to 0 if overflow ids are not available
// during replay. Otherwise |first_overflow_id| must be larger than any id
// referred to in |binary_in| or |transformation_sequence_in|, and overflow
// ids will be available during replay starting from this value.
//
// The transformations that were successfully applied are returned via
// |transformation_sequence_out|.
ReplayerResultStatus Run(
const std::vector<uint32_t>& binary_in,
const protobufs::FactSequence& initial_facts,
const protobufs::TransformationSequence& transformation_sequence_in,
uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
uint32_t num_transformations_to_apply, uint32_t first_overflow_id,
std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const;
private:

Просмотреть файл

@ -18,6 +18,8 @@
#include "source/fuzz/pseudo_random_generator.h"
#include "source/fuzz/replayer.h"
#include "source/opt/build_module.h"
#include "source/opt/ir_context.h"
#include "source/spirv_fuzzer_options.h"
#include "source/util/make_unique.h"
@ -67,6 +69,10 @@ struct Shrinker::Impl {
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.
@ -76,6 +82,14 @@ struct Shrinker::Impl {
spv_validator_options validator_options; // Options to control validation.
};
uint32_t Shrinker::Impl::GetIdBound(const std::vector<uint32_t>& binary) {
// Build the module from the input binary.
std::unique_ptr<opt::IRContext> ir_context =
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,
bool validate_during_replay,
spv_validator_options validator_options)
@ -99,7 +113,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
// header files being used.
GOOGLE_PROTOBUF_VERIFY_VERSION;
spvtools::SpirvTools tools(impl_->target_env);
SpirvTools tools(impl_->target_env);
if (!tools.IsValid()) {
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
"Failed to create SPIRV-Tools interface; stopping.");
@ -127,7 +141,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
if (replayer.Run(binary_in, initial_facts, transformation_sequence_in,
static_cast<uint32_t>(
transformation_sequence_in.transformation_size()),
&current_best_binary, &current_best_transformations) !=
/* No overflow ids */ 0, &current_best_binary,
&current_best_transformations) !=
Replayer::ReplayerResultStatus::kComplete) {
return ShrinkerResultStatus::kReplayFailed;
}
@ -140,6 +155,12 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
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) &&
"Applying transformations should only increase a module's id bound.");
uint32_t attempt = 0; // Keeps track of the number of shrink attempts that
// have been tried, whether successful or not.
@ -201,7 +222,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
binary_in, initial_facts, transformations_with_chunk_removed,
static_cast<uint32_t>(
transformations_with_chunk_removed.transformation_size()),
&next_binary, &next_transformation_sequence) !=
first_overflow_id, &next_binary, &next_transformation_sequence) !=
Replayer::ReplayerResultStatus::kComplete) {
// Replay should not fail; if it does, we need to abort shrinking.
return ShrinkerResultStatus::kReplayFailed;

Просмотреть файл

@ -14,12 +14,43 @@
#include "source/fuzz/transformation_context.h"
#include <cassert>
#include "source/util/make_unique.h"
namespace spvtools {
namespace fuzz {
namespace {
// An overflow id source that should never be used: its methods assert false.
// This is the right id source for use during fuzzing, when overflow ids should
// never be required.
class NullOverflowIdSource : public OverflowIdSource {
bool HasOverflowIds() const override {
assert(false && "Bad attempt to query whether overflow ids are available.");
return false;
}
uint32_t GetNextOverflowId() override {
assert(false && "Bad attempt to request an overflow id.");
return 0;
}
};
} // namespace
TransformationContext::TransformationContext(
FactManager* fact_manager, spv_validator_options validator_options)
: fact_manager_(fact_manager), validator_options_(validator_options) {}
: fact_manager_(fact_manager),
validator_options_(validator_options),
overflow_id_source_(MakeUnique<NullOverflowIdSource>()) {}
TransformationContext::TransformationContext(
FactManager* fact_manager, spv_validator_options validator_options,
std::unique_ptr<OverflowIdSource> overflow_id_source)
: fact_manager_(fact_manager),
validator_options_(validator_options),
overflow_id_source_(std::move(overflow_id_source)) {}
TransformationContext::~TransformationContext() = default;

Просмотреть файл

@ -15,7 +15,10 @@
#ifndef SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_
#define SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_
#include <memory>
#include "source/fuzz/fact_manager/fact_manager.h"
#include "source/fuzz/overflow_id_source.h"
#include "spirv-tools/libspirv.hpp"
namespace spvtools {
@ -26,16 +29,29 @@ namespace fuzz {
class TransformationContext {
public:
// Constructs a transformation context with a given fact manager and validator
// options.
// options. Overflow ids are not available from a transformation context
// constructed in this way.
TransformationContext(FactManager* fact_manager,
spv_validator_options validator_options);
// Constructs a transformation context with a given fact manager, validator
// options and overflow id source.
TransformationContext(FactManager* fact_manager,
spv_validator_options validator_options,
std::unique_ptr<OverflowIdSource> overflow_id_source);
~TransformationContext();
FactManager* GetFactManager() { return fact_manager_; }
const FactManager* GetFactManager() const { return fact_manager_; }
OverflowIdSource* GetOverflowIdSource() { return overflow_id_source_.get(); }
const OverflowIdSource* GetOverflowIdSource() const {
return overflow_id_source_.get();
}
spv_validator_options GetValidatorOptions() const {
return validator_options_;
}
@ -48,6 +64,8 @@ class TransformationContext {
// Options to control validation when deciding whether transformations can be
// applied.
spv_validator_options validator_options_;
std::unique_ptr<OverflowIdSource> overflow_id_source_;
};
} // namespace fuzz

Просмотреть файл

@ -48,7 +48,8 @@ TransformationOutlineFunction::TransformationOutlineFunction(
}
bool TransformationOutlineFunction::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
std::set<uint32_t> ids_used_by_this_transformation;
// The various new ids used by the transformation must be fresh and distinct.
@ -234,8 +235,9 @@ bool TransformationOutlineFunction::IsApplicable(
fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id());
for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) {
// There needs to be a corresponding fresh id to be used as a function
// parameter.
if (input_id_to_fresh_id_map.count(id) == 0) {
// parameter, or overflow ids need to be available.
if (input_id_to_fresh_id_map.count(id) == 0 &&
!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
return false;
}
// Furthermore, if the input id has pointer type it must be an OpVariable
@ -263,8 +265,10 @@ bool TransformationOutlineFunction::IsApplicable(
for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) {
if (
// ... there needs to be a corresponding fresh id that can hold the
// value for this id computed in the outlined function, and ...
output_id_to_fresh_id_map.count(id) == 0
// value for this id computed in the outlined function (or overflow ids
// must be available), and ...
(output_id_to_fresh_id_map.count(id) == 0 &&
!transformation_context.GetOverflowIdSource()->HasOverflowIds())
// ... the output id must not have pointer type (to avoid creating a
// struct with pointer members to pass data out of the outlined
// function)
@ -306,6 +310,23 @@ void TransformationOutlineFunction::Apply(
auto output_id_to_fresh_id_map =
fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id());
// Use overflow ids to augment these maps at any locations where fresh ids are
// required but not provided.
for (uint32_t id : region_input_ids) {
if (input_id_to_fresh_id_map.count(id) == 0) {
input_id_to_fresh_id_map.insert(
{id,
transformation_context->GetOverflowIdSource()->GetNextOverflowId()});
}
}
for (uint32_t id : region_output_ids) {
if (output_id_to_fresh_id_map.count(id) == 0) {
output_id_to_fresh_id_map.insert(
{id,
transformation_context->GetOverflowIdSource()->GetNextOverflowId()});
}
}
UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map,
output_id_to_fresh_id_map);
@ -609,7 +630,7 @@ TransformationOutlineFunction::PrepareFunctionPrototype(
{function_type_id}}})));
// Add one parameter to the function for each input id, using the fresh ids
// provided in |input_id_to_fresh_id_map|.
// provided in |input_id_to_fresh_id_map|, or overflow ids if needed.
for (auto id : region_input_ids) {
outlined_function->AddParameter(MakeUnique<opt::Instruction>(
ir_context, SpvOpFunctionParameter,

Просмотреть файл

@ -1661,7 +1661,7 @@ void RunFuzzerAndReplayer(const std::string& shader,
binary_in, initial_facts, fuzzer_transformation_sequence_out,
static_cast<uint32_t>(
fuzzer_transformation_sequence_out.transformation_size()),
&replayer_binary_out, &replayer_transformation_sequence_out);
0, &replayer_binary_out, &replayer_transformation_sequence_out);
ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
replayer_result_status);

Просмотреть файл

@ -95,8 +95,8 @@ TEST(ReplayerTest, PartialReplay) {
Replayer replayer(env, true, validator_options);
replayer.SetMessageConsumer(kSilentConsumer);
auto replayer_result_status =
replayer.Run(binary_in, empty_facts, transformations, 11, &binary_out,
&transformations_out);
replayer.Run(binary_in, empty_facts, transformations, 11, 0,
&binary_out, &transformations_out);
// Replay should succeed.
ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
replayer_result_status);
@ -183,7 +183,7 @@ TEST(ReplayerTest, PartialReplay) {
Replayer replayer(env, true, validator_options);
replayer.SetMessageConsumer(kSilentConsumer);
auto replayer_result_status =
replayer.Run(binary_in, empty_facts, transformations, 5, &binary_out,
replayer.Run(binary_in, empty_facts, transformations, 5, 0, &binary_out,
&transformations_out);
// Replay should succeed.
ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
@ -263,7 +263,7 @@ TEST(ReplayerTest, PartialReplay) {
Replayer replayer(env, true, validator_options);
replayer.SetMessageConsumer(kSilentConsumer);
auto replayer_result_status =
replayer.Run(binary_in, empty_facts, transformations, 0, &binary_out,
replayer.Run(binary_in, empty_facts, transformations, 0, 0, &binary_out,
&transformations_out);
// Replay should succeed.
ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
@ -283,8 +283,8 @@ TEST(ReplayerTest, PartialReplay) {
Replayer replayer(env, true, validator_options);
replayer.SetMessageConsumer(kSilentConsumer);
auto replayer_result_status =
replayer.Run(binary_in, empty_facts, transformations, 12, &binary_out,
&transformations_out);
replayer.Run(binary_in, empty_facts, transformations, 12, 0,
&binary_out, &transformations_out);
// Replay should not succeed.
ASSERT_EQ(Replayer::ReplayerResultStatus::kTooManyTransformationsRequested,

Просмотреть файл

@ -13,6 +13,8 @@
// limitations under the License.
#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/counter_overflow_id_source.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@ -2579,9 +2581,10 @@ TEST(TransformationOutlineFunctionTest, ExitBlockHeadsLoop) {
}
TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
// This tests outlining of some non-trivial code.
// This tests outlining of some non-trivial code, and also tests the way
// overflow ids are used by the transformation.
std::string shader = R"(
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@ -2682,13 +2685,7 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
TransformationOutlineFunction transformation(
/*entry_block*/ 150,
@ -2702,123 +2699,317 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous1) {
/*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
/*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationOutlineFunction transformation_with_missing_input_id(
/*entry_block*/ 150,
/*exit_block*/ 1001,
/*new_function_struct_return_type_id*/ 200,
/*new_function_type_id*/ 201,
/*new_function_id*/ 202,
/*new_function_region_entry_block*/ 203,
/*new_caller_result_id*/ 204,
/*new_callee_result_id*/ 205,
/*input_id_to_fresh_id*/ {{102, 300}, {40, 302}},
/*output_id_to_fresh_id*/ {{106, 400}, {107, 401}});
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %85
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %28 "buf"
OpMemberName %28 0 "u1"
OpMemberName %28 1 "u2"
OpName %30 ""
OpName %85 "color"
OpMemberDecorate %28 0 Offset 0
OpMemberDecorate %28 1 Offset 4
OpDecorate %28 Block
OpDecorate %30 DescriptorSet 0
OpDecorate %30 Binding 0
OpDecorate %85 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%10 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpConstant %6 3
%13 = OpConstant %6 4
%14 = OpConstantComposite %7 %10 %11 %12 %13
%15 = OpTypeInt 32 1
%18 = OpConstant %15 0
%28 = OpTypeStruct %6 %6
%29 = OpTypePointer Uniform %28
%30 = OpVariable %29 Uniform
%31 = OpTypePointer Uniform %6
%35 = OpTypeBool
%39 = OpConstant %15 1
%84 = OpTypePointer Output %7
%85 = OpVariable %84 Output
%114 = OpConstant %15 8
%200 = OpTypeStruct %7 %15
%201 = OpTypeFunction %200 %15 %7 %15
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %22
%22 = OpLabel
%103 = OpPhi %15 %18 %5 %106 %43
%102 = OpPhi %7 %14 %5 %107 %43
%101 = OpPhi %15 %18 %5 %40 %43
%32 = OpAccessChain %31 %30 %18
%33 = OpLoad %6 %32
%34 = OpConvertFToS %15 %33
%36 = OpSLessThan %35 %101 %34
OpLoopMerge %24 %43 None
OpBranchConditional %36 %23 %24
%23 = OpLabel
%40 = OpIAdd %15 %101 %39
OpBranch %150
%150 = OpLabel
%204 = OpFunctionCall %200 %202 %103 %102 %40
%107 = OpCompositeExtract %7 %204 0
%106 = OpCompositeExtract %15 %204 1
OpBranch %43
%43 = OpLabel
OpBranch %22
%24 = OpLabel
%87 = OpCompositeExtract %6 %102 0
%91 = OpConvertSToF %6 %103
%92 = OpCompositeConstruct %7 %87 %11 %91 %10
OpStore %85 %92
OpReturn
OpFunctionEnd
%202 = OpFunction %200 None %201
%301 = OpFunctionParameter %15
%300 = OpFunctionParameter %7
%302 = OpFunctionParameter %15
%203 = OpLabel
OpBranch %41
%41 = OpLabel
%401 = OpPhi %7 %300 %203 %111 %65
%400 = OpPhi %15 %301 %203 %110 %65
%104 = OpPhi %15 %302 %203 %81 %65
%47 = OpAccessChain %31 %30 %39
%48 = OpLoad %6 %47
%49 = OpConvertFToS %15 %48
%50 = OpSLessThan %35 %104 %49
OpLoopMerge %1000 %65 None
OpBranchConditional %50 %42 %1000
%42 = OpLabel
%60 = OpIAdd %15 %400 %114
%63 = OpSGreaterThan %35 %104 %60
OpBranchConditional %63 %64 %65
%64 = OpLabel
%71 = OpCompositeExtract %6 %401 0
%72 = OpFAdd %6 %71 %11
%97 = OpCompositeInsert %7 %72 %401 0
%76 = OpCompositeExtract %6 %401 3
%77 = OpConvertFToS %15 %76
%79 = OpIAdd %15 %60 %77
OpBranch %65
%65 = OpLabel
%111 = OpPhi %7 %401 %42 %97 %64
%110 = OpPhi %15 %60 %42 %79 %64
%81 = OpIAdd %15 %104 %39
OpBranch %41
%1000 = OpLabel
OpBranch %1001
%1001 = OpLabel
%205 = OpCompositeConstruct %200 %401 %400
OpReturnValue %205
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
TransformationOutlineFunction transformation_with_missing_output_id(
/*entry_block*/ 150,
/*exit_block*/ 1001,
/*new_function_struct_return_type_id*/ 200,
/*new_function_type_id*/ 201,
/*new_function_id*/ 202,
/*new_function_region_entry_block*/ 203,
/*new_caller_result_id*/ 204,
/*new_callee_result_id*/ 205,
/*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}},
/*output_id_to_fresh_id*/ {{106, 400}});
TransformationOutlineFunction
transformation_with_missing_input_and_output_ids(
/*entry_block*/ 150,
/*exit_block*/ 1001,
/*new_function_struct_return_type_id*/ 200,
/*new_function_type_id*/ 201,
/*new_function_id*/ 202,
/*new_function_region_entry_block*/ 203,
/*new_caller_result_id*/ 204,
/*new_callee_result_id*/ 205,
/*input_id_to_fresh_id*/ {{102, 300}, {40, 302}},
/*output_id_to_fresh_id*/ {{106, 400}});
{
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
TransformationContext transformation_context(&fact_manager,
validator_options);
#ifndef NDEBUG
// We expect the following applicability checks to lead to assertion
// failures since the transformations are missing input or output ids, and
// the transformation context does not have a source of overflow ids.
ASSERT_DEATH(transformation_with_missing_input_id.IsApplicable(
context.get(), transformation_context),
"Bad attempt to query whether overflow ids are available.");
ASSERT_DEATH(transformation_with_missing_output_id.IsApplicable(
context.get(), transformation_context),
"Bad attempt to query whether overflow ids are available.");
ASSERT_DEATH(transformation_with_missing_input_and_output_ids.IsApplicable(
context.get(), transformation_context),
"Bad attempt to query whether overflow ids are available.");
#endif
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
std::string variant_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %85
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %28 "buf"
OpMemberName %28 0 "u1"
OpMemberName %28 1 "u2"
OpName %30 ""
OpName %85 "color"
OpMemberDecorate %28 0 Offset 0
OpMemberDecorate %28 1 Offset 4
OpDecorate %28 Block
OpDecorate %30 DescriptorSet 0
OpDecorate %30 Binding 0
OpDecorate %85 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%10 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpConstant %6 3
%13 = OpConstant %6 4
%14 = OpConstantComposite %7 %10 %11 %12 %13
%15 = OpTypeInt 32 1
%18 = OpConstant %15 0
%28 = OpTypeStruct %6 %6
%29 = OpTypePointer Uniform %28
%30 = OpVariable %29 Uniform
%31 = OpTypePointer Uniform %6
%35 = OpTypeBool
%39 = OpConstant %15 1
%84 = OpTypePointer Output %7
%85 = OpVariable %84 Output
%114 = OpConstant %15 8
%200 = OpTypeStruct %7 %15
%201 = OpTypeFunction %200 %15 %7 %15
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %22
%22 = OpLabel
%103 = OpPhi %15 %18 %5 %106 %43
%102 = OpPhi %7 %14 %5 %107 %43
%101 = OpPhi %15 %18 %5 %40 %43
%32 = OpAccessChain %31 %30 %18
%33 = OpLoad %6 %32
%34 = OpConvertFToS %15 %33
%36 = OpSLessThan %35 %101 %34
OpLoopMerge %24 %43 None
OpBranchConditional %36 %23 %24
%23 = OpLabel
%40 = OpIAdd %15 %101 %39
OpBranch %150
%150 = OpLabel
%204 = OpFunctionCall %200 %202 %103 %102 %40
%107 = OpCompositeExtract %7 %204 0
%106 = OpCompositeExtract %15 %204 1
OpBranch %43
%43 = OpLabel
OpBranch %22
%24 = OpLabel
%87 = OpCompositeExtract %6 %102 0
%91 = OpConvertSToF %6 %103
%92 = OpCompositeConstruct %7 %87 %11 %91 %10
OpStore %85 %92
OpReturn
OpFunctionEnd
%202 = OpFunction %200 None %201
%301 = OpFunctionParameter %15
%300 = OpFunctionParameter %7
%302 = OpFunctionParameter %15
%203 = OpLabel
OpBranch %41
%41 = OpLabel
%401 = OpPhi %7 %300 %203 %111 %65
%400 = OpPhi %15 %301 %203 %110 %65
%104 = OpPhi %15 %302 %203 %81 %65
%47 = OpAccessChain %31 %30 %39
%48 = OpLoad %6 %47
%49 = OpConvertFToS %15 %48
%50 = OpSLessThan %35 %104 %49
OpLoopMerge %1000 %65 None
OpBranchConditional %50 %42 %1000
%42 = OpLabel
%60 = OpIAdd %15 %400 %114
%63 = OpSGreaterThan %35 %104 %60
OpBranchConditional %63 %64 %65
%64 = OpLabel
%71 = OpCompositeExtract %6 %401 0
%72 = OpFAdd %6 %71 %11
%97 = OpCompositeInsert %7 %72 %401 0
%76 = OpCompositeExtract %6 %401 3
%77 = OpConvertFToS %15 %76
%79 = OpIAdd %15 %60 %77
OpBranch %65
%65 = OpLabel
%111 = OpPhi %7 %401 %42 %97 %64
%110 = OpPhi %15 %60 %42 %79 %64
%81 = OpIAdd %15 %104 %39
OpBranch %41
%1000 = OpLabel
OpBranch %1001
%1001 = OpLabel
%205 = OpCompositeConstruct %200 %401 %400
OpReturnValue %205
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
}
{
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
TransformationContext new_transformation_context(
&fact_manager, validator_options,
MakeUnique<CounterOverflowIdSource>(2000));
ASSERT_TRUE(transformation_with_missing_input_id.IsApplicable(
context.get(), new_transformation_context));
ASSERT_TRUE(transformation_with_missing_output_id.IsApplicable(
context.get(), new_transformation_context));
ASSERT_TRUE(transformation_with_missing_input_and_output_ids.IsApplicable(
context.get(), new_transformation_context));
transformation_with_missing_input_and_output_ids.Apply(
context.get(), &new_transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
std::string variant_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %85
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %28 "buf"
OpMemberName %28 0 "u1"
OpMemberName %28 1 "u2"
OpName %30 ""
OpName %85 "color"
OpMemberDecorate %28 0 Offset 0
OpMemberDecorate %28 1 Offset 4
OpDecorate %28 Block
OpDecorate %30 DescriptorSet 0
OpDecorate %30 Binding 0
OpDecorate %85 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%10 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpConstant %6 3
%13 = OpConstant %6 4
%14 = OpConstantComposite %7 %10 %11 %12 %13
%15 = OpTypeInt 32 1
%18 = OpConstant %15 0
%28 = OpTypeStruct %6 %6
%29 = OpTypePointer Uniform %28
%30 = OpVariable %29 Uniform
%31 = OpTypePointer Uniform %6
%35 = OpTypeBool
%39 = OpConstant %15 1
%84 = OpTypePointer Output %7
%85 = OpVariable %84 Output
%114 = OpConstant %15 8
%200 = OpTypeStruct %7 %15
%201 = OpTypeFunction %200 %15 %7 %15
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %22
%22 = OpLabel
%103 = OpPhi %15 %18 %5 %106 %43
%102 = OpPhi %7 %14 %5 %107 %43
%101 = OpPhi %15 %18 %5 %40 %43
%32 = OpAccessChain %31 %30 %18
%33 = OpLoad %6 %32
%34 = OpConvertFToS %15 %33
%36 = OpSLessThan %35 %101 %34
OpLoopMerge %24 %43 None
OpBranchConditional %36 %23 %24
%23 = OpLabel
%40 = OpIAdd %15 %101 %39
OpBranch %150
%150 = OpLabel
%204 = OpFunctionCall %200 %202 %103 %102 %40
%107 = OpCompositeExtract %7 %204 0
%106 = OpCompositeExtract %15 %204 1
OpBranch %43
%43 = OpLabel
OpBranch %22
%24 = OpLabel
%87 = OpCompositeExtract %6 %102 0
%91 = OpConvertSToF %6 %103
%92 = OpCompositeConstruct %7 %87 %11 %91 %10
OpStore %85 %92
OpReturn
OpFunctionEnd
%202 = OpFunction %200 None %201
%2000 = OpFunctionParameter %15
%300 = OpFunctionParameter %7
%302 = OpFunctionParameter %15
%203 = OpLabel
OpBranch %41
%41 = OpLabel
%2001 = OpPhi %7 %300 %203 %111 %65
%400 = OpPhi %15 %2000 %203 %110 %65
%104 = OpPhi %15 %302 %203 %81 %65
%47 = OpAccessChain %31 %30 %39
%48 = OpLoad %6 %47
%49 = OpConvertFToS %15 %48
%50 = OpSLessThan %35 %104 %49
OpLoopMerge %1000 %65 None
OpBranchConditional %50 %42 %1000
%42 = OpLabel
%60 = OpIAdd %15 %400 %114
%63 = OpSGreaterThan %35 %104 %60
OpBranchConditional %63 %64 %65
%64 = OpLabel
%71 = OpCompositeExtract %6 %2001 0
%72 = OpFAdd %6 %71 %11
%97 = OpCompositeInsert %7 %72 %2001 0
%76 = OpCompositeExtract %6 %2001 3
%77 = OpConvertFToS %15 %76
%79 = OpIAdd %15 %60 %77
OpBranch %65
%65 = OpLabel
%111 = OpPhi %7 %2001 %42 %97 %64
%110 = OpPhi %15 %60 %42 %79 %64
%81 = OpIAdd %15 %104 %39
OpBranch %41
%1000 = OpLabel
OpBranch %1001
%1001 = OpLabel
%205 = OpCompositeConstruct %200 %2001 %400
OpReturnValue %205
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
}
}
TEST(TransformationOutlineFunctionTest, Miscellaneous2) {

Просмотреть файл

@ -428,7 +428,7 @@ bool Replay(const spv_target_env& target_env,
auto replay_result_status = replayer.Run(
binary_in, initial_facts, transformation_sequence,
num_transformations_to_apply, binary_out, transformations_applied);
num_transformations_to_apply, 0, binary_out, transformations_applied);
return !(replay_result_status !=
spvtools::fuzz::Replayer::ReplayerResultStatus::kComplete);
}