mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
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:
parent
1cea3b7853
commit
253806adc4
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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*)>&
|
||||
|
121
source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
Normal file
121
source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
Normal 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
|
39
source/fuzz/fuzzer_pass_adjust_loop_controls.h
Normal file
39
source/fuzz/fuzzer_pass_adjust_loop_controls.h
Normal 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_
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
|
216
source/fuzz/transformation_set_loop_control.cpp
Normal file
216
source/fuzz/transformation_set_loop_control.cpp
Normal 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
|
79
source/fuzz/transformation_set_loop_control.h
Normal file
79
source/fuzz/transformation_set_loop_control.h
Normal 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_
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
968
test/fuzz/transformation_set_loop_control_test.cpp
Normal file
968
test/fuzz/transformation_set_loop_control_test.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user