spirv-fuzz: Add fuzzer pass to change loop controls (#2949)

A new pass that allows the fuzzer to change the 'loop control' operand
(and associated literal operands) of OpLoopMerge instructions.

Fixes #2938.
Fixes #2943.
This commit is contained in:
Alastair Donaldson 2019-10-10 13:34:38 +01:00 committed by GitHub
parent 1cea3b7853
commit 253806adc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1501 additions and 11 deletions

View File

@ -38,6 +38,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_dead_continues.h
fuzzer_pass_add_useful_constructs.h
fuzzer_pass_adjust_loop_controls.h
fuzzer_pass_adjust_selection_controls.h
fuzzer_pass_apply_id_synonyms.h
fuzzer_pass_construct_composites.h
@ -67,6 +68,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h
transformation_set_loop_control.h
transformation_set_selection_control.h
transformation_split_block.h
uniform_buffer_element_descriptor.h
@ -81,6 +83,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_dead_continues.cpp
fuzzer_pass_add_useful_constructs.cpp
fuzzer_pass_adjust_loop_controls.cpp
fuzzer_pass_adjust_selection_controls.cpp
fuzzer_pass_apply_id_synonyms.cpp
fuzzer_pass_construct_composites.cpp
@ -109,6 +112,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp
transformation_set_loop_control.cpp
transformation_set_selection_control.cpp
transformation_split_block.cpp
uniform_buffer_element_descriptor.cpp

View File

@ -23,6 +23,7 @@
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
#include "source/fuzz/fuzzer_pass_construct_composites.h"
@ -169,6 +170,11 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
// Now apply some passes that it does not make sense to apply repeatedly,
// as they do not unlock other passes.
if (fuzzer_context.ChooseEven()) {
FuzzerPassAdjustLoopControls(ir_context.get(), &fact_manager,
&fuzzer_context, transformation_sequence_out)
.Apply();
}
if (fuzzer_context.ChooseEven()) {
FuzzerPassAdjustSelectionControls(ir_context.get(), &fact_manager,
&fuzzer_context,

View File

@ -25,6 +25,7 @@ namespace {
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
90};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
@ -34,6 +35,11 @@ const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
// Default limits for various quantities that are chosen during fuzzing.
// Keep them in alphabetical order.
const uint32_t kDefaultMaxLoopControlPartialCount = 100;
const uint32_t kDefaultMaxLoopControlPeelCount = 100;
// Default functions for controlling how deep to go during recursive
// generation/transformation. Keep them in alphabetical order.
@ -56,6 +62,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
chance_of_adding_dead_continue_ =
ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
chance_of_adjusting_loop_control_ =
ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
chance_of_adjusting_selection_control_ =
ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
chance_of_constructing_composite_ =
@ -68,6 +76,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
chance_of_replacing_id_with_synonym_ =
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount;
max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount;
}
FuzzerContext::~FuzzerContext() = default;

View File

@ -62,6 +62,9 @@ class FuzzerContext {
uint32_t GetChanceOfAddingDeadContinue() {
return chance_of_adding_dead_continue_;
}
uint32_t GetChanceOfAdjustingLoopControl() {
return chance_of_adjusting_loop_control_;
}
uint32_t GetChanceOfAdjustingSelectionControl() {
return chance_of_adjusting_selection_control_;
}
@ -77,6 +80,12 @@ class FuzzerContext {
return chance_of_replacing_id_with_synonym_;
}
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
uint32_t GetRandomLoopControlPeelCount() {
return random_generator_->RandomUint32(max_loop_control_peel_count_);
}
uint32_t GetRandomLoopControlPartialCount() {
return random_generator_->RandomUint32(max_loop_control_partial_count_);
}
// Functions to control how deeply to recurse.
// Keep them in alphabetical order.
@ -94,6 +103,7 @@ class FuzzerContext {
// Keep them in alphabetical order.
uint32_t chance_of_adding_dead_break_;
uint32_t chance_of_adding_dead_continue_;
uint32_t chance_of_adjusting_loop_control_;
uint32_t chance_of_adjusting_selection_control_;
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
@ -102,6 +112,12 @@ class FuzzerContext {
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_splitting_block_;
// Limits associated with various quantities for which random values are
// chosen during fuzzing.
// Keep them in alphabetical order.
uint32_t max_loop_control_partial_count_;
uint32_t max_loop_control_peel_count_;
// Functions to determine with what probability to go deeper when generating
// or mutating constructs recursively.
const std::function<bool(uint32_t, RandomGenerator*)>&

View File

@ -0,0 +1,121 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
#include "source/fuzz/transformation_set_loop_control.h"
namespace spvtools {
namespace fuzz {
FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default;
void FuzzerPassAdjustLoopControls::Apply() {
// Consider every merge instruction in the module (via looking through all
// functions and blocks).
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
if (auto merge_inst = block.GetMergeInst()) {
// Ignore the instruction if it is not a loop merge.
if (merge_inst->opcode() != SpvOpLoopMerge) {
continue;
}
// Decide randomly whether to adjust this loop merge.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) {
continue;
}
uint32_t existing_mask = merge_inst->GetSingleWordOperand(
TransformationSetLoopControl::kLoopControlMaskInOperandIndex);
// First, set the new mask to one of None, Unroll or DontUnroll.
std::vector<uint32_t> basic_masks = {SpvLoopControlMaskNone,
SpvLoopControlUnrollMask,
SpvLoopControlDontUnrollMask};
uint32_t new_mask =
basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)];
// For the loop controls that depend on guarantees about what the loop
// does, check which of these were present in the existing mask and
// randomly decide whether to keep them. They are just hints, so
// removing them should not change the semantics of the module.
for (auto mask_bit :
{SpvLoopControlDependencyInfiniteMask,
SpvLoopControlDependencyLengthMask,
SpvLoopControlMinIterationsMask, SpvLoopControlMaxIterationsMask,
SpvLoopControlIterationMultipleMask}) {
if ((existing_mask & mask_bit) && GetFuzzerContext()->ChooseEven()) {
// The mask bits we are considering are not available in all SPIR-V
// versions. However, we only include a mask bit if it was present
// in the original loop control mask, and we work under the
// assumption that we are transforming a valid module, thus we don't
// need to actually check whether the SPIR-V version being used
// supports these loop control mask bits.
new_mask |= mask_bit;
}
}
// We use 0 for peel count and partial count in the case that we choose
// not to set these controls.
uint32_t peel_count = 0;
uint32_t partial_count = 0;
// PeelCount and PartialCount are not compatible with DontUnroll, so
// we check whether DontUnroll is set.
if (!(new_mask & SpvLoopControlDontUnrollMask)) {
// If PeelCount is supported by this SPIR-V version, randomly choose
// whether to set it. If it was set in the original mask and is not
// selected for setting here, that amounts to dropping it.
if (TransformationSetLoopControl::PeelCountIsSupported(
GetIRContext()) &&
GetFuzzerContext()->ChooseEven()) {
new_mask |= SpvLoopControlPeelCountMask;
// The peel count is chosen randomly - if PeelCount was already set
// this will overwrite whatever peel count was previously used.
peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount();
}
// Similar, but for PartialCount.
if (TransformationSetLoopControl::PartialCountIsSupported(
GetIRContext()) &&
GetFuzzerContext()->ChooseEven()) {
new_mask |= SpvLoopControlPartialCountMask;
partial_count =
GetFuzzerContext()->GetRandomLoopControlPartialCount();
}
}
// Apply the transformation and add it to the output transformation
// sequence.
TransformationSetLoopControl transformation(block.id(), new_mask,
peel_count, partial_count);
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Transformation should be applicable by construction.");
transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
transformation.ToMessage();
}
}
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,39 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// A pass that adjusts the loop controls on OpLoopMerge instructions.
class FuzzerPassAdjustLoopControls : public FuzzerPass {
public:
FuzzerPassAdjustLoopControls(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAdjustLoopControls() override;
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_

View File

@ -23,8 +23,8 @@ package spvtools.fuzz.protobufs;
message IdUseDescriptor {
// Describes a use of an id as an input operand to an instruction in some block
// of a function.
// Describes a use of an id as an input operand to an instruction in some
// block of a function.
// Example:
// - id_of_interest = 42
@ -167,7 +167,8 @@ message Transformation {
TransformationAddTypeFloat add_type_float = 6;
TransformationAddTypeInt add_type_int = 7;
TransformationAddDeadBreak add_dead_break = 8;
TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9;
TransformationReplaceBooleanConstantWithConstantBinary
replace_boolean_constant_with_constant_binary = 9;
TransformationAddTypePointer add_type_pointer = 10;
TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
TransformationAddDeadContinue add_dead_continue = 12;
@ -175,6 +176,7 @@ message Transformation {
TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
TransformationSetSelectionControl set_selection_control = 15;
TransformationConstructComposite construct_composite = 16;
TransformationSetLoopControl set_loop_control = 17;
// Add additional option using the next available number.
}
}
@ -410,18 +412,42 @@ message TransformationReplaceIdWithSynonym {
uint32 fresh_id_for_temporary = 3;
}
message TransformationSetLoopControl {
// A transformation that sets the loop control operand of an OpLoopMerge
// instruction.
// The id of a basic block that should contain OpLoopMerge
uint32 block_id = 1;
// The value to which the 'loop control' operand should be set.
// This must be a legal loop control mask.
uint32 loop_control = 2;
// Provides a peel count value for the loop. Used if and only if the
// PeelCount bit is set. Must be zero if the PeelCount bit is not set (can
// still be zero if this bit is set).
uint32 peel_count = 3;
// Provides a partial count value for the loop. Used if and only if the
// PartialCount bit is set. Must be zero if the PartialCount bit is not set
// (can still be zero if this bit is set).
uint32 partial_count = 4;
}
message TransformationSetSelectionControl {
// A transformation that sets the selection control operand of an
// OpSelectionMerge instruction.
// The id of a basic block that should contain OpSelectionMerge.
// The id of a basic block that should contain OpSelectionMerge
uint32 block_id = 1;
// The value to which the 'selection control' operand should be set.
// Although technically 'selection control' is a literal mask that can be
// some combination of 'None', 'Flatten' and 'DontFlatten', the combination
// 'Flatten | DontFlatten' does not make sense.
// 'Flatten | DontFlatten' does not make sense and is not allowed here.
uint32 selection_control = 2;
}

View File

@ -30,6 +30,7 @@
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
#include "source/fuzz/transformation_set_loop_control.h"
#include "source/fuzz/transformation_set_selection_control.h"
#include "source/fuzz/transformation_split_block.h"
#include "source/util/make_unique.h"
@ -81,6 +82,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
return MakeUnique<TransformationReplaceIdWithSynonym>(
message.replace_id_with_synonym());
case protobufs::Transformation::TransformationCase::kSetLoopControl:
return MakeUnique<TransformationSetLoopControl>(
message.set_loop_control());
case protobufs::Transformation::TransformationCase::kSetSelectionControl:
return MakeUnique<TransformationSetSelectionControl>(
message.set_selection_control());

View File

@ -0,0 +1,216 @@
// Copyright (c) 2019 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_set_loop_control.h"
namespace spvtools {
namespace fuzz {
TransformationSetLoopControl::TransformationSetLoopControl(
const spvtools::fuzz::protobufs::TransformationSetLoopControl& message)
: message_(message) {}
TransformationSetLoopControl::TransformationSetLoopControl(
uint32_t block_id, uint32_t loop_control, uint32_t peel_count,
uint32_t partial_count) {
message_.set_block_id(block_id);
message_.set_loop_control(loop_control);
message_.set_peel_count(peel_count);
message_.set_partial_count(partial_count);
}
bool TransformationSetLoopControl::IsApplicable(
opt::IRContext* context, const FactManager& /*unused*/) const {
// |message_.block_id| must identify a block that ends with OpLoopMerge.
auto block = context->get_instr_block(message_.block_id());
if (!block) {
return false;
}
auto merge_inst = block->GetMergeInst();
if (!merge_inst || merge_inst->opcode() != SpvOpLoopMerge) {
return false;
}
// We sanity-check that the transformation does not try to set any meaningless
// bits of the loop control mask.
uint32_t all_loop_control_mask_bits_set =
SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask |
SpvLoopControlDependencyInfiniteMask |
SpvLoopControlDependencyLengthMask | SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask | SpvLoopControlIterationMultipleMask |
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask;
// The variable is only used in an assertion; the following keeps release-mode
// compilers happy.
(void)(all_loop_control_mask_bits_set);
// No additional bits should be set.
assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set));
// Grab the loop control mask currently associated with the OpLoopMerge
// instruction.
auto existing_loop_control_mask =
merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
// Check that there is no attempt to set one of the loop controls that
// requires guarantees to hold.
for (SpvLoopControlMask mask :
{SpvLoopControlDependencyInfiniteMask,
SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
// We have a problem if this loop control bit was not set in the original
// loop control mask but is set by the transformation.
if (LoopControlBitIsAddedByTransformation(mask,
existing_loop_control_mask)) {
return false;
}
}
if ((message_.loop_control() &
(SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) &&
!(PeelCountIsSupported(context) && PartialCountIsSupported(context))) {
// At least one of PeelCount or PartialCount is used, but the SPIR-V version
// in question does not support these loop controls.
return false;
}
if (message_.peel_count() > 0 &&
!(message_.loop_control() & SpvLoopControlPeelCountMask)) {
// Peel count provided, but peel count mask bit not set.
return false;
}
if (message_.partial_count() > 0 &&
!(message_.loop_control() & SpvLoopControlPartialCountMask)) {
// Partial count provided, but partial count mask bit not set.
return false;
}
// We must not set both 'don't unroll' and one of 'peel count' or 'partial
// count'.
return !((message_.loop_control() & SpvLoopControlDontUnrollMask) &&
(message_.loop_control() &
(SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)));
}
void TransformationSetLoopControl::Apply(opt::IRContext* context,
FactManager* /*unused*/) const {
// Grab the loop merge instruction and its associated loop control mask.
auto merge_inst =
context->get_instr_block(message_.block_id())->GetMergeInst();
auto existing_loop_control_mask =
merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
// We are going to replace the OpLoopMerge's operands with this list.
opt::Instruction::OperandList new_operands;
// We add the existing merge block and continue target ids.
new_operands.push_back(merge_inst->GetInOperand(0));
new_operands.push_back(merge_inst->GetInOperand(1));
// We use the loop control mask from the transformation.
new_operands.push_back(
{SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
// It remains to determine what literals to provide, in association with
// the new loop control mask.
//
// For the loop controls that require guarantees to hold about the number
// of loop iterations, we need to keep, from the original OpLoopMerge, any
// literals associated with loop control bits that are still set.
uint32_t literal_index = 0; // Indexes into the literals from the original
// instruction.
for (SpvLoopControlMask mask :
{SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
// Check whether the bit was set in the original loop control mask.
if (existing_loop_control_mask & mask) {
// Check whether the bit is set in the new loop control mask.
if (message_.loop_control() & mask) {
// Add the associated literal to our sequence of replacement operands.
new_operands.push_back(
{SPV_OPERAND_TYPE_LITERAL_INTEGER,
{merge_inst->GetSingleWordInOperand(
kLoopControlFirstLiteralInOperandIndex + literal_index)}});
}
// Increment our index into the original loop control mask's literals,
// whether or not the bit was set in the new mask.
literal_index++;
}
}
// If PeelCount is set in the new mask, |message_.peel_count| provides the
// associated peel count.
if (message_.loop_control() & SpvLoopControlPeelCountMask) {
new_operands.push_back(
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
}
// Similar, but for PartialCount.
if (message_.loop_control() & SpvLoopControlPartialCountMask) {
new_operands.push_back(
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
}
// Replace the input operands of the OpLoopMerge with the new operands we have
// accumulated.
merge_inst->SetInOperands(std::move(new_operands));
}
protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
protobufs::Transformation result;
*result.mutable_set_loop_control() = message_;
return result;
}
bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
SpvLoopControlMask loop_control_single_bit_mask,
uint32_t existing_loop_control_mask) const {
return !(loop_control_single_bit_mask & existing_loop_control_mask) &&
(loop_control_single_bit_mask & message_.loop_control());
}
bool TransformationSetLoopControl::PartialCountIsSupported(
opt::IRContext* context) {
// TODO(afd): We capture the universal environments for which this loop
// control is definitely not supported. The check should be refined on
// demand for other target environments.
switch (context->grammar().target_env()) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
return false;
default:
return true;
}
}
bool TransformationSetLoopControl::PeelCountIsSupported(
opt::IRContext* context) {
// TODO(afd): We capture the universal environments for which this loop
// control is definitely not supported. The check should be refined on
// demand for other target environments.
switch (context->grammar().target_env()) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
return false;
default:
return true;
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,79 @@
// Copyright (c) 2019 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_SET_LOOP_CONTROL_H_
#define SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationSetLoopControl : public Transformation {
public:
const static uint32_t kLoopControlMaskInOperandIndex = 2;
const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3;
explicit TransformationSetLoopControl(
const protobufs::TransformationSetLoopControl& message);
TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control,
uint32_t peel_count, uint32_t partial_count);
// - |message_.block_id| must be a block containing an OpLoopMerge
// instruction.
// - |message_.loop_control| must be a legal loop control mask that
// only uses controls available in the SPIR-V version associated with
// |context|, and must not add loop controls that are only valid in the
// presence of guarantees about what the loop does (e.g. MinIterations).
// - |message_.peel_count| (respectively |message_.partial_count|) must be
// zero PeelCount (respectively PartialCount) is set in
// |message_.loop_control|.
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// - The loop control operand of the OpLoopMergeInstruction in
// |message_.block_id| is overwritten with |message_.loop_control|.
// - The literals associated with the loop control are updated to reflect any
// controls with associated literals that have been removed (e.g.
// MinIterations), and any that have been added (PeelCount and/or
// PartialCount).
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
// Does the version of SPIR-V being used support the PartialCount loop
// control?
static bool PartialCountIsSupported(opt::IRContext* context);
// Does the version of SPIR-V being used support the PeelCount loop control?
static bool PeelCountIsSupported(opt::IRContext* context);
private:
// Returns true if and only if |loop_single_bit_mask| is *not* set in
// |existing_loop_control| but *is* set in |message_.loop_control|.
bool LoopControlBitIsAddedByTransformation(
SpvLoopControlMask loop_control_single_bit_mask,
uint32_t existing_loop_control_mask) const;
protobufs::TransformationSetLoopControl message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_

View File

@ -34,6 +34,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp
transformation_replace_id_with_synonym_test.cpp
transformation_set_loop_control_test.cpp
transformation_set_selection_control_test.cpp
transformation_split_block_test.cpp
uniform_buffer_element_descriptor_test.cpp)

View File

@ -31,7 +31,7 @@ const uint32_t kNumFuzzerRuns = 20;
void RunFuzzerAndReplayer(const std::string& shader,
const protobufs::FactSequence& initial_facts,
uint32_t initial_seed, uint32_t num_runs) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto env = SPV_ENV_UNIVERSAL_1_5;
std::vector<uint32_t> binary_in;
SpirvTools t(env);
@ -305,7 +305,7 @@ TEST(FuzzerReplayerTest, Miscellaneous2) {
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %16 %139
OpEntryPoint Fragment %4 "main" %16 %139 %25 %68
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
@ -605,7 +605,7 @@ TEST(FuzzerReplayerTest, Miscellaneous3) {
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %68 %100
OpEntryPoint Fragment %4 "main" %68 %100 %24
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"

View File

@ -154,7 +154,7 @@ void RunAndCheckShrinker(
void RunFuzzerAndShrinker(const std::string& shader,
const protobufs::FactSequence& initial_facts,
uint32_t seed) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto env = SPV_ENV_UNIVERSAL_1_5;
std::vector<uint32_t> binary_in;
SpirvTools t(env);
@ -434,7 +434,7 @@ TEST(FuzzerShrinkerTest, Miscellaneous2) {
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %16 %139
OpEntryPoint Fragment %4 "main" %16 %139 %25 %68
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
@ -732,7 +732,7 @@ TEST(FuzzerShrinkerTest, Miscellaneous3) {
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %68 %100
OpEntryPoint Fragment %4 "main" %68 %100 %24
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"

View File

@ -0,0 +1,968 @@
// Copyright (c) 2019 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_set_loop_control.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationSetLoopControlTest, VariousScenarios) {
// This test features loops with various different controls, and goes through
// a number of acceptable and unacceptable transformations to those controls.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%22 = OpVariable %7 Function
%32 = OpVariable %7 Function
%42 = OpVariable %7 Function
%52 = OpVariable %7 Function
%62 = OpVariable %7 Function
%72 = OpVariable %7 Function
%82 = OpVariable %7 Function
%92 = OpVariable %7 Function
%102 = OpVariable %7 Function
%112 = OpVariable %7 Function
%122 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%132 = OpPhi %6 %9 %5 %21 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %132 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%21 = OpIAdd %6 %132 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpStore %22 %9
OpBranch %23
%23 = OpLabel
%133 = OpPhi %6 %9 %12 %31 %26
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %17 %133 %16
OpBranchConditional %29 %24 %25
%24 = OpLabel
OpBranch %26
%26 = OpLabel
%31 = OpIAdd %6 %133 %20
OpStore %22 %31
OpBranch %23
%25 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
%134 = OpPhi %6 %9 %25 %41 %36
OpLoopMerge %35 %36 DontUnroll
OpBranch %37
%37 = OpLabel
%39 = OpSLessThan %17 %134 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpBranch %36
%36 = OpLabel
%41 = OpIAdd %6 %134 %20
OpStore %32 %41
OpBranch %33
%35 = OpLabel
OpStore %42 %9
OpBranch %43
%43 = OpLabel
%135 = OpPhi %6 %9 %35 %51 %46
OpLoopMerge %45 %46 DependencyInfinite
OpBranch %47
%47 = OpLabel
%49 = OpSLessThan %17 %135 %16
OpBranchConditional %49 %44 %45
%44 = OpLabel
OpBranch %46
%46 = OpLabel
%51 = OpIAdd %6 %135 %20
OpStore %42 %51
OpBranch %43
%45 = OpLabel
OpStore %52 %9
OpBranch %53
%53 = OpLabel
%136 = OpPhi %6 %9 %45 %61 %56
OpLoopMerge %55 %56 DependencyLength 3
OpBranch %57
%57 = OpLabel
%59 = OpSLessThan %17 %136 %16
OpBranchConditional %59 %54 %55
%54 = OpLabel
OpBranch %56
%56 = OpLabel
%61 = OpIAdd %6 %136 %20
OpStore %52 %61
OpBranch %53
%55 = OpLabel
OpStore %62 %9
OpBranch %63
%63 = OpLabel
%137 = OpPhi %6 %9 %55 %71 %66
OpLoopMerge %65 %66 MinIterations 10
OpBranch %67
%67 = OpLabel
%69 = OpSLessThan %17 %137 %16
OpBranchConditional %69 %64 %65
%64 = OpLabel
OpBranch %66
%66 = OpLabel
%71 = OpIAdd %6 %137 %20
OpStore %62 %71
OpBranch %63
%65 = OpLabel
OpStore %72 %9
OpBranch %73
%73 = OpLabel
%138 = OpPhi %6 %9 %65 %81 %76
OpLoopMerge %75 %76 MaxIterations 50
OpBranch %77
%77 = OpLabel
%79 = OpSLessThan %17 %138 %16
OpBranchConditional %79 %74 %75
%74 = OpLabel
OpBranch %76
%76 = OpLabel
%81 = OpIAdd %6 %138 %20
OpStore %72 %81
OpBranch %73
%75 = OpLabel
OpStore %82 %9
OpBranch %83
%83 = OpLabel
%139 = OpPhi %6 %9 %75 %91 %86
OpLoopMerge %85 %86 IterationMultiple 4
OpBranch %87
%87 = OpLabel
%89 = OpSLessThan %17 %139 %16
OpBranchConditional %89 %84 %85
%84 = OpLabel
OpBranch %86
%86 = OpLabel
%91 = OpIAdd %6 %139 %20
OpStore %82 %91
OpBranch %83
%85 = OpLabel
OpStore %92 %9
OpBranch %93
%93 = OpLabel
%140 = OpPhi %6 %9 %85 %101 %96
OpLoopMerge %95 %96 PeelCount 2
OpBranch %97
%97 = OpLabel
%99 = OpSLessThan %17 %140 %16
OpBranchConditional %99 %94 %95
%94 = OpLabel
OpBranch %96
%96 = OpLabel
%101 = OpIAdd %6 %140 %20
OpStore %92 %101
OpBranch %93
%95 = OpLabel
OpStore %102 %9
OpBranch %103
%103 = OpLabel
%141 = OpPhi %6 %9 %95 %111 %106
OpLoopMerge %105 %106 PartialCount 3
OpBranch %107
%107 = OpLabel
%109 = OpSLessThan %17 %141 %16
OpBranchConditional %109 %104 %105
%104 = OpLabel
OpBranch %106
%106 = OpLabel
%111 = OpIAdd %6 %141 %20
OpStore %102 %111
OpBranch %103
%105 = OpLabel
OpStore %112 %9
OpBranch %113
%113 = OpLabel
%142 = OpPhi %6 %9 %105 %121 %116
OpLoopMerge %115 %116 Unroll|PeelCount|PartialCount 3 4
OpBranch %117
%117 = OpLabel
%119 = OpSLessThan %17 %142 %16
OpBranchConditional %119 %114 %115
%114 = OpLabel
OpBranch %116
%116 = OpLabel
%121 = OpIAdd %6 %142 %20
OpStore %112 %121
OpBranch %113
%115 = OpLabel
OpStore %122 %9
OpBranch %123
%123 = OpLabel
%143 = OpPhi %6 %9 %115 %131 %126
OpLoopMerge %125 %126 DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount 2 5 90 4 7 14
OpBranch %127
%127 = OpLabel
%129 = OpSLessThan %17 %143 %16
OpBranchConditional %129 %124 %125
%124 = OpLabel
OpBranch %126
%126 = OpLabel
%131 = OpIAdd %6 %143 %20
OpStore %122 %131
OpBranch %123
%125 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// These are the loop headers together with the selection controls of their
// merge instructions:
// %10 None
// %23 Unroll
// %33 DontUnroll
// %43 DependencyInfinite
// %53 DependencyLength 3
// %63 MinIterations 10
// %73 MaxIterations 50
// %83 IterationMultiple 4
// %93 PeelCount 2
// %103 PartialCount 3
// %113 Unroll|PeelCount|PartialCount 3 4
// %123
// DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount
// 2 5 90 4 7 14
ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
10, SpvLoopControlDependencyInfiniteMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
10, SpvLoopControlIterationMultipleMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
10,
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(10,
SpvLoopControlUnrollMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(10,
SpvLoopControlDontUnrollMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
23,
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(33,
SpvLoopControlDontUnrollMask |
SpvLoopControlPartialCountMask,
0, 10)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
43,
SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
43,
SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(43,
SpvLoopControlDependencyInfiniteMask |
SpvLoopControlDependencyLengthMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(
53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(53,
SpvLoopControlDependencyInfiniteMask |
SpvLoopControlDependencyLengthMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
53,
SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask |
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
63,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask,
2, 23)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
73,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(73,
SpvLoopControlUnrollMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
73,
SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask |
SpvLoopControlPeelCountMask,
2, 23)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
83,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(83,
SpvLoopControlUnrollMask |
SpvLoopControlIterationMultipleMask |
SpvLoopControlPeelCountMask,
23, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(83,
SpvLoopControlUnrollMask |
SpvLoopControlIterationMultipleMask |
SpvLoopControlPeelCountMask,
2, 23)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
93,
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
16, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(103,
SpvLoopControlDontUnrollMask |
SpvLoopControlPartialCountMask,
0, 60)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(
113,
SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12,
0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
123,
SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask |
SpvLoopControlIterationMultipleMask |
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
7, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(123,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPartialCountMask,
0, 9)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
123,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPartialCountMask,
7, 9)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(
123,
SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
7, 9)
.IsApplicable(context.get(), fact_manager));
TransformationSetLoopControl(10,
SpvLoopControlUnrollMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
3, 3)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(
43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(63,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(73,
SpvLoopControlUnrollMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(
93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(
123,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
0, 9)
.Apply(context.get(), &fact_manager);
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%22 = OpVariable %7 Function
%32 = OpVariable %7 Function
%42 = OpVariable %7 Function
%52 = OpVariable %7 Function
%62 = OpVariable %7 Function
%72 = OpVariable %7 Function
%82 = OpVariable %7 Function
%92 = OpVariable %7 Function
%102 = OpVariable %7 Function
%112 = OpVariable %7 Function
%122 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%132 = OpPhi %6 %9 %5 %21 %13
OpLoopMerge %12 %13 Unroll|PeelCount|PartialCount 3 3
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %132 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%21 = OpIAdd %6 %132 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpStore %22 %9
OpBranch %23
%23 = OpLabel
%133 = OpPhi %6 %9 %12 %31 %26
OpLoopMerge %25 %26 DontUnroll
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %17 %133 %16
OpBranchConditional %29 %24 %25
%24 = OpLabel
OpBranch %26
%26 = OpLabel
%31 = OpIAdd %6 %133 %20
OpStore %22 %31
OpBranch %23
%25 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
%134 = OpPhi %6 %9 %25 %41 %36
OpLoopMerge %35 %36 Unroll
OpBranch %37
%37 = OpLabel
%39 = OpSLessThan %17 %134 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpBranch %36
%36 = OpLabel
%41 = OpIAdd %6 %134 %20
OpStore %32 %41
OpBranch %33
%35 = OpLabel
OpStore %42 %9
OpBranch %43
%43 = OpLabel
%135 = OpPhi %6 %9 %35 %51 %46
OpLoopMerge %45 %46 DontUnroll|DependencyInfinite
OpBranch %47
%47 = OpLabel
%49 = OpSLessThan %17 %135 %16
OpBranchConditional %49 %44 %45
%44 = OpLabel
OpBranch %46
%46 = OpLabel
%51 = OpIAdd %6 %135 %20
OpStore %42 %51
OpBranch %43
%45 = OpLabel
OpStore %52 %9
OpBranch %53
%53 = OpLabel
%136 = OpPhi %6 %9 %45 %61 %56
OpLoopMerge %55 %56 None
OpBranch %57
%57 = OpLabel
%59 = OpSLessThan %17 %136 %16
OpBranchConditional %59 %54 %55
%54 = OpLabel
OpBranch %56
%56 = OpLabel
%61 = OpIAdd %6 %136 %20
OpStore %52 %61
OpBranch %53
%55 = OpLabel
OpStore %62 %9
OpBranch %63
%63 = OpLabel
%137 = OpPhi %6 %9 %55 %71 %66
OpLoopMerge %65 %66 Unroll|MinIterations|PeelCount 10 23
OpBranch %67
%67 = OpLabel
%69 = OpSLessThan %17 %137 %16
OpBranchConditional %69 %64 %65
%64 = OpLabel
OpBranch %66
%66 = OpLabel
%71 = OpIAdd %6 %137 %20
OpStore %62 %71
OpBranch %63
%65 = OpLabel
OpStore %72 %9
OpBranch %73
%73 = OpLabel
%138 = OpPhi %6 %9 %65 %81 %76
OpLoopMerge %75 %76 Unroll|MaxIterations|PeelCount 50 23
OpBranch %77
%77 = OpLabel
%79 = OpSLessThan %17 %138 %16
OpBranchConditional %79 %74 %75
%74 = OpLabel
OpBranch %76
%76 = OpLabel
%81 = OpIAdd %6 %138 %20
OpStore %72 %81
OpBranch %73
%75 = OpLabel
OpStore %82 %9
OpBranch %83
%83 = OpLabel
%139 = OpPhi %6 %9 %75 %91 %86
OpLoopMerge %85 %86 DontUnroll
OpBranch %87
%87 = OpLabel
%89 = OpSLessThan %17 %139 %16
OpBranchConditional %89 %84 %85
%84 = OpLabel
OpBranch %86
%86 = OpLabel
%91 = OpIAdd %6 %139 %20
OpStore %82 %91
OpBranch %83
%85 = OpLabel
OpStore %92 %9
OpBranch %93
%93 = OpLabel
%140 = OpPhi %6 %9 %85 %101 %96
OpLoopMerge %95 %96 PeelCount|PartialCount 16 8
OpBranch %97
%97 = OpLabel
%99 = OpSLessThan %17 %140 %16
OpBranchConditional %99 %94 %95
%94 = OpLabel
OpBranch %96
%96 = OpLabel
%101 = OpIAdd %6 %140 %20
OpStore %92 %101
OpBranch %93
%95 = OpLabel
OpStore %102 %9
OpBranch %103
%103 = OpLabel
%141 = OpPhi %6 %9 %95 %111 %106
OpLoopMerge %105 %106 PartialCount 60
OpBranch %107
%107 = OpLabel
%109 = OpSLessThan %17 %141 %16
OpBranchConditional %109 %104 %105
%104 = OpLabel
OpBranch %106
%106 = OpLabel
%111 = OpIAdd %6 %141 %20
OpStore %102 %111
OpBranch %103
%105 = OpLabel
OpStore %112 %9
OpBranch %113
%113 = OpLabel
%142 = OpPhi %6 %9 %105 %121 %116
OpLoopMerge %115 %116 PeelCount 12
OpBranch %117
%117 = OpLabel
%119 = OpSLessThan %17 %142 %16
OpBranchConditional %119 %114 %115
%114 = OpLabel
OpBranch %116
%116 = OpLabel
%121 = OpIAdd %6 %142 %20
OpStore %112 %121
OpBranch %113
%115 = OpLabel
OpStore %122 %9
OpBranch %123
%123 = OpLabel
%143 = OpPhi %6 %9 %115 %131 %126
OpLoopMerge %125 %126 Unroll|MinIterations|MaxIterations|PartialCount 5 90 9
OpBranch %127
%127 = OpLabel
%129 = OpSLessThan %17 %143 %16
OpBranchConditional %129 %124 %125
%124 = OpLabel
OpBranch %126
%126 = OpLabel
%131 = OpIAdd %6 %143 %20
OpStore %122 %131
OpBranch %123
%125 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) {
// This test checks that we do not allow introducing PeelCount and
// PartialCount loop controls if the SPIR-V version being used does not
// support them.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto consumer = nullptr;
const auto context_1_0 =
BuildModule(SPV_ENV_UNIVERSAL_1_0, consumer, shader, kFuzzAssembleOption);
const auto context_1_1 =
BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer, shader, kFuzzAssembleOption);
const auto context_1_2 =
BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer, shader, kFuzzAssembleOption);
const auto context_1_3 =
BuildModule(SPV_ENV_UNIVERSAL_1_3, consumer, shader, kFuzzAssembleOption);
const auto context_1_4 =
BuildModule(SPV_ENV_UNIVERSAL_1_4, consumer, shader, kFuzzAssembleOption);
const auto context_1_5 =
BuildModule(SPV_ENV_UNIVERSAL_1_5, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
TransformationSetLoopControl set_peel_and_partial(
10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4);
// PeelCount and PartialCount were introduced in SPIRV 1.4, so are not valid
// in the context of older versions.
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_0.get(), fact_manager));
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_1.get(), fact_manager));
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_2.get(), fact_manager));
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_3.get(), fact_manager));
ASSERT_TRUE(
set_peel_and_partial.IsApplicable(context_1_4.get(), fact_manager));
ASSERT_TRUE(
set_peel_and_partial.IsApplicable(context_1_5.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools