mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated spirv-tools.
This commit is contained in:
4
3rdparty/spirv-tools/BUILD.gn
vendored
4
3rdparty/spirv-tools/BUILD.gn
vendored
@@ -734,10 +734,6 @@ static_library("spvtools_reduce") {
|
||||
"source/reduce/remove_function_reduction_opportunity_finder.h",
|
||||
"source/reduce/remove_instruction_reduction_opportunity.cpp",
|
||||
"source/reduce/remove_instruction_reduction_opportunity.h",
|
||||
"source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp",
|
||||
"source/reduce/remove_opname_instruction_reduction_opportunity_finder.h",
|
||||
"source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp",
|
||||
"source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h",
|
||||
"source/reduce/remove_selection_reduction_opportunity.cpp",
|
||||
"source/reduce/remove_selection_reduction_opportunity.h",
|
||||
"source/reduce/remove_selection_reduction_opportunity_finder.cpp",
|
||||
|
||||
43
3rdparty/spirv-tools/CHANGES
vendored
43
3rdparty/spirv-tools/CHANGES
vendored
@@ -1,7 +1,11 @@
|
||||
Revision history for SPIRV-Tools
|
||||
|
||||
v2019.5-dev 2019-08-08
|
||||
v2019.5-dev 2019-10-09
|
||||
- General:
|
||||
- Export SPIRV-Tools targets on installation
|
||||
- SPIRV-Tools support for SPIR-V 1.5 (#2865)
|
||||
- Add WebGPU SPIR-V Assembler in JavaScript. (#2876)
|
||||
- Add Bazel build configuration. (#2891)
|
||||
- Optimizer
|
||||
- Add descriptor array scalar replacement (#2742)
|
||||
- Add pass to wrap OpKill in a function call (#2790)
|
||||
@@ -9,17 +13,44 @@ v2019.5-dev 2019-08-08
|
||||
- Add pass to replace AMD shader ballot extension (#2811)
|
||||
- Add pass to make Float32 operation relax precision (#2808)
|
||||
- Add pass to make relax precision operation Float16 (#2808)
|
||||
- Add pass to replace uses of 3 AMD extensions (#2814)
|
||||
- Fold Min, Max, and Clamp instructions. (#2836)
|
||||
- Better handling of OpKill in continues (#2842,#2922,#2933)
|
||||
- Enable OpTypeCooperativeMatrix specialization (#2927)
|
||||
Fixes:
|
||||
Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
|
||||
Instrument: Add support for Buffer Device Address extension (#2792)
|
||||
Fix check for changed binary in API call. (#2798)
|
||||
- Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
|
||||
- Instrument: Add support for Buffer Device Address extension (#2792)
|
||||
- Fix check for changed binary in API call. (#2798)
|
||||
- For WebGPU<->Vulkan optimization, set correct execution environment (#2834)
|
||||
- Handle OpConstantNull in copy-prop-arrays. (#2870)
|
||||
- Use OpReturn* in wrap-opkill (#2886)
|
||||
- Validator
|
||||
- Add generic builtin validation of target (#2843)
|
||||
- Extra resource interface validation (#2864)
|
||||
- Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862)
|
||||
- Relaxed bitcast with pointers (#2878)
|
||||
- Validate physical storage buffer restrictions (#2930)
|
||||
- Add SPV_KHR_shader_clock validation (#2879)
|
||||
Fixes:
|
||||
Fix validation of constant matrices (#2794)
|
||||
Update "remquor" validation
|
||||
- Fix validation of constant matrices (#2794)
|
||||
- Update "remquor" validation
|
||||
- Only allow previously declared forward refs in structs (#2920)
|
||||
- Reduce
|
||||
- Remove relaxed precision decorations (#2797)
|
||||
- Reduce/fuzz: improve command line args (#2932)
|
||||
- Improve remove unref instr pass (#2945)
|
||||
Fixes:
|
||||
- Fuzz
|
||||
- Fix add-dead-break and add-dead-continue passes to respect dominance (#2838)
|
||||
- Add fuzzer pass to copy objects (#2853)
|
||||
- Add fuzzer pass to replace ids with synonyms (#2857)
|
||||
- Allow validation during spirv-fuzz replay (#2873)
|
||||
- Employ the "swarm testing" idea in spirv-fuzz (#2890)
|
||||
- reduce/fuzz: improve command line args (#2932)
|
||||
- option to convert shader into a form that renders red (#2934)
|
||||
- Add fuzzer pass to change selection controls (#2944)
|
||||
- add transformation and pass to construct composites (#2941)
|
||||
|
||||
|
||||
v2019.4 2019-08-08
|
||||
- General:
|
||||
|
||||
@@ -1 +1 @@
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-102-gc18c9ff6"
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-120-g2276e597"
|
||||
|
||||
22
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
22
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
@@ -37,14 +37,20 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass.h
|
||||
fuzzer_pass_add_dead_breaks.h
|
||||
fuzzer_pass_add_dead_continues.h
|
||||
fuzzer_pass_add_no_contraction_decorations.h
|
||||
fuzzer_pass_add_useful_constructs.h
|
||||
fuzzer_pass_adjust_function_controls.h
|
||||
fuzzer_pass_adjust_loop_controls.h
|
||||
fuzzer_pass_adjust_selection_controls.h
|
||||
fuzzer_pass_apply_id_synonyms.h
|
||||
fuzzer_pass_construct_composites.h
|
||||
fuzzer_pass_copy_objects.h
|
||||
fuzzer_pass_obfuscate_constants.h
|
||||
fuzzer_pass_permute_blocks.h
|
||||
fuzzer_pass_split_blocks.h
|
||||
fuzzer_util.h
|
||||
id_use_descriptor.h
|
||||
instruction_descriptor.h
|
||||
protobufs/spirvfuzz_protobufs.h
|
||||
pseudo_random_generator.h
|
||||
random_generator.h
|
||||
@@ -55,15 +61,20 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_constant_scalar.h
|
||||
transformation_add_dead_break.h
|
||||
transformation_add_dead_continue.h
|
||||
transformation_add_no_contraction_decoration.h
|
||||
transformation_add_type_boolean.h
|
||||
transformation_add_type_float.h
|
||||
transformation_add_type_int.h
|
||||
transformation_add_type_pointer.h
|
||||
transformation_construct_composite.h
|
||||
transformation_copy_object.h
|
||||
transformation_move_block_down.h
|
||||
transformation_replace_boolean_constant_with_constant_binary.h
|
||||
transformation_replace_constant_with_uniform.h
|
||||
transformation_replace_id_with_synonym.h
|
||||
transformation_set_function_control.h
|
||||
transformation_set_loop_control.h
|
||||
transformation_set_selection_control.h
|
||||
transformation_split_block.h
|
||||
uniform_buffer_element_descriptor.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
|
||||
@@ -76,14 +87,20 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass.cpp
|
||||
fuzzer_pass_add_dead_breaks.cpp
|
||||
fuzzer_pass_add_dead_continues.cpp
|
||||
fuzzer_pass_add_no_contraction_decorations.cpp
|
||||
fuzzer_pass_add_useful_constructs.cpp
|
||||
fuzzer_pass_adjust_function_controls.cpp
|
||||
fuzzer_pass_adjust_loop_controls.cpp
|
||||
fuzzer_pass_adjust_selection_controls.cpp
|
||||
fuzzer_pass_apply_id_synonyms.cpp
|
||||
fuzzer_pass_construct_composites.cpp
|
||||
fuzzer_pass_copy_objects.cpp
|
||||
fuzzer_pass_obfuscate_constants.cpp
|
||||
fuzzer_pass_permute_blocks.cpp
|
||||
fuzzer_pass_split_blocks.cpp
|
||||
fuzzer_util.cpp
|
||||
id_use_descriptor.cpp
|
||||
instruction_descriptor.cpp
|
||||
pseudo_random_generator.cpp
|
||||
random_generator.cpp
|
||||
replayer.cpp
|
||||
@@ -93,15 +110,20 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_constant_scalar.cpp
|
||||
transformation_add_dead_break.cpp
|
||||
transformation_add_dead_continue.cpp
|
||||
transformation_add_no_contraction_decoration.cpp
|
||||
transformation_add_type_boolean.cpp
|
||||
transformation_add_type_float.cpp
|
||||
transformation_add_type_int.cpp
|
||||
transformation_add_type_pointer.cpp
|
||||
transformation_construct_composite.cpp
|
||||
transformation_copy_object.cpp
|
||||
transformation_move_block_down.cpp
|
||||
transformation_replace_boolean_constant_with_constant_binary.cpp
|
||||
transformation_replace_constant_with_uniform.cpp
|
||||
transformation_replace_id_with_synonym.cpp
|
||||
transformation_set_function_control.cpp
|
||||
transformation_set_loop_control.cpp
|
||||
transformation_set_selection_control.cpp
|
||||
transformation_split_block.cpp
|
||||
uniform_buffer_element_descriptor.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "source/fuzz/force_render_red.h"
|
||||
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
@@ -147,9 +148,10 @@ MakeConstantUniformReplacement(opt::IRContext* ir_context,
|
||||
uint32_t greater_than_instruction,
|
||||
uint32_t in_operand_index) {
|
||||
return MakeUnique<TransformationReplaceConstantWithUniform>(
|
||||
transformation::MakeIdUseDescriptor(constant_id, SpvOpFOrdGreaterThan,
|
||||
in_operand_index,
|
||||
greater_than_instruction, 0),
|
||||
MakeIdUseDescriptor(constant_id,
|
||||
MakeInstructionDescriptor(greater_than_instruction,
|
||||
SpvOpFOrdGreaterThan, 0),
|
||||
in_operand_index),
|
||||
fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0],
|
||||
ir_context->TakeNextId(), ir_context->TakeNextId());
|
||||
}
|
||||
|
||||
31
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
31
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@@ -22,8 +22,13 @@
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_function_controls.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"
|
||||
#include "source/fuzz/fuzzer_pass_copy_objects.h"
|
||||
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
|
||||
@@ -138,6 +143,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassConstructComposites>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
@@ -162,6 +170,29 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
passes[fuzzer_context.RandomIndex(passes)]->Apply();
|
||||
}
|
||||
|
||||
// Now apply some passes that it does not make sense to apply repeatedly,
|
||||
// as they do not unlock other passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> final_passes;
|
||||
MaybeAddPass<FuzzerPassAdjustFunctionControls>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustLoopControls>(&passes, ir_context.get(),
|
||||
&fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
|
||||
&passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
for (auto& pass : final_passes) {
|
||||
pass->Apply();
|
||||
}
|
||||
|
||||
if (fuzzer_context.ChooseEven()) {
|
||||
FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager,
|
||||
&fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
}
|
||||
|
||||
// Encode the module as a binary.
|
||||
ir_context->module()->ToBinary(binary_out, false);
|
||||
|
||||
|
||||
@@ -25,12 +25,25 @@ 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> kChanceOfAddingNoContractionDecoration = {
|
||||
5, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
|
||||
70};
|
||||
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};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
|
||||
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.
|
||||
|
||||
@@ -53,6 +66,16 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
|
||||
chance_of_adding_dead_continue_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
|
||||
chance_of_adding_no_contraction_decoration_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
|
||||
chance_of_adjusting_function_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
|
||||
chance_of_adjusting_loop_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
|
||||
chance_of_adjusting_selection_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
|
||||
chance_of_constructing_composite_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
|
||||
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
|
||||
chance_of_moving_block_down_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
|
||||
@@ -61,6 +84,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,21 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfAddingDeadContinue() {
|
||||
return chance_of_adding_dead_continue_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingNoContractionDecoration() {
|
||||
return chance_of_adding_no_contraction_decoration_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingFunctionControl() {
|
||||
return chance_of_adjusting_function_control_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingLoopControl() {
|
||||
return chance_of_adjusting_loop_control_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingSelectionControl() {
|
||||
return chance_of_adjusting_selection_control_;
|
||||
}
|
||||
uint32_t GetChanceOfConstructingComposite() {
|
||||
return chance_of_constructing_composite_;
|
||||
}
|
||||
uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
|
||||
uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
|
||||
uint32_t GetChanceOfObfuscatingConstant() {
|
||||
@@ -71,6 +86,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.
|
||||
@@ -88,12 +109,23 @@ 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_adding_no_contraction_decoration_;
|
||||
uint32_t chance_of_adjusting_function_control_;
|
||||
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_;
|
||||
uint32_t chance_of_moving_block_down_;
|
||||
uint32_t chance_of_obfuscating_constant_;
|
||||
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*)>&
|
||||
|
||||
99
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
99
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
@@ -27,5 +27,104 @@ FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
|
||||
FuzzerPass::~FuzzerPass() = default;
|
||||
|
||||
std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
|
||||
const opt::Function& function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
std::function<bool(opt::IRContext*, opt::Instruction*)>
|
||||
instruction_is_relevant) {
|
||||
// TODO(afd) The following is (relatively) simple, but may end up being
|
||||
// prohibitively inefficient, as it walks the whole dominator tree for
|
||||
// every instruction that is considered.
|
||||
|
||||
std::vector<opt::Instruction*> result;
|
||||
// Consider all global declarations
|
||||
for (auto& global : GetIRContext()->module()->types_values()) {
|
||||
if (instruction_is_relevant(GetIRContext(), &global)) {
|
||||
result.push_back(&global);
|
||||
}
|
||||
}
|
||||
|
||||
// Consider all previous instructions in this block
|
||||
for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it;
|
||||
++prev_inst_it) {
|
||||
if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) {
|
||||
result.push_back(&*prev_inst_it);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the dominator tree to consider all instructions from dominating
|
||||
// blocks
|
||||
auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function);
|
||||
for (auto next_dominator = dominator_analysis->ImmediateDominator(block);
|
||||
next_dominator != nullptr;
|
||||
next_dominator =
|
||||
dominator_analysis->ImmediateDominator(next_dominator)) {
|
||||
for (auto& dominating_inst : *next_dominator) {
|
||||
if (instruction_is_relevant(GetIRContext(), &dominating_inst)) {
|
||||
result.push_back(&dominating_inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
|
||||
std::function<uint32_t(
|
||||
const opt::Function& function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)>
|
||||
maybe_apply_transformation) {
|
||||
// Consider every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// We now consider every instruction in the block, randomly deciding
|
||||
// whether to apply a transformation before it.
|
||||
|
||||
// In order for transformations to insert new instructions, they need to
|
||||
// be able to identify the instruction to insert before. We enable this
|
||||
// by tracking a base instruction, which must generate a result id, and
|
||||
// an offset (to allow us to identify instructions that do not generate
|
||||
// result ids).
|
||||
|
||||
// The initial base instruction is the block label.
|
||||
uint32_t base = block.id();
|
||||
uint32_t offset = 0;
|
||||
// Consider every instruction in the block.
|
||||
for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
|
||||
if (inst_it->HasResultId()) {
|
||||
// In the case that the instruction has a result id, we use the
|
||||
// instruction as its own base, with zero offset.
|
||||
base = inst_it->result_id();
|
||||
offset = 0;
|
||||
} else {
|
||||
// The instruction does not have a result id, so we need to identify
|
||||
// it via the latest instruction that did have a result id (base), and
|
||||
// an incremented offset.
|
||||
offset++;
|
||||
}
|
||||
|
||||
// Invoke the provided function, which might apply a transformation.
|
||||
// Its return value informs us of how many instructions it inserted.
|
||||
// (This will be 0 if no transformation was applied.)
|
||||
uint32_t num_instructions_inserted =
|
||||
maybe_apply_transformation(function, &block, inst_it, base, offset);
|
||||
|
||||
if (!inst_it->HasResultId()) {
|
||||
// We are tracking the current id-less instruction via an offset,
|
||||
// |offset|, from a previous instruction, |base|, that has an id. We
|
||||
// increment |offset| to reflect any newly-inserted instructions.
|
||||
//
|
||||
// An alternative would be to reset |base| to be an id generated by
|
||||
// a newly-inserted instruction, but that would be more complex, and
|
||||
// sticking to a |base| that already existed before this
|
||||
// transformation was applied makes the applicability of future
|
||||
// transformations less tightly coupled with the presence of the just-
|
||||
// applied transformation.
|
||||
offset += num_instructions_inserted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
42
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
42
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
@@ -15,9 +15,13 @@
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_H_
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@@ -48,6 +52,44 @@ class FuzzerPass {
|
||||
return transformations_;
|
||||
}
|
||||
|
||||
// Returns all instructions that are *available* at |inst_it|, which is
|
||||
// required to be inside block |block| of function |function| - that is, all
|
||||
// instructions at global scope and all instructions that strictly dominate
|
||||
// |inst_it|.
|
||||
//
|
||||
// Filters said instructions to return only those that satisfy the
|
||||
// |instruction_is_relevant| predicate. This, for instance, could ignore all
|
||||
// instructions that have a particular decoration.
|
||||
std::vector<opt::Instruction*> FindAvailableInstructions(
|
||||
const opt::Function& function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
std::function<bool(opt::IRContext*, opt::Instruction*)>
|
||||
instruction_is_relevant);
|
||||
|
||||
// A helper method that iterates through each instruction in each block, at
|
||||
// all times tracking a base instruction and offset that allows that latest
|
||||
// instruction to be located even if it has no result id.
|
||||
//
|
||||
// The code to manipulate the base and offset is a bit fiddly, and the point
|
||||
// of this method is to avoiding having to duplicate it in multiple
|
||||
// transformation passes.
|
||||
//
|
||||
// The function |maybe_apply_transformation| is invoked for each instruction
|
||||
// |inst_it| in block |block| of function |function| that is encountered. The
|
||||
// |base| and |offset| parameters to the function object allow |inst_it| to be
|
||||
// identified.
|
||||
//
|
||||
// The job of |maybe_apply_transformation| is to randomly decide whether to
|
||||
// try to apply some transformation, and then - if selected - to attempt to
|
||||
// apply it. The function returns the number of instructions that were
|
||||
// inserted before |inst_it|, so that |offset| can be updated.
|
||||
//
|
||||
void MaybeAddTransformationBeforeEachInstruction(
|
||||
std::function<uint32_t(
|
||||
const opt::Function& function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)>
|
||||
maybe_apply_transformation);
|
||||
|
||||
private:
|
||||
opt::IRContext* ir_context_;
|
||||
FactManager* fact_manager_;
|
||||
|
||||
61
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
vendored
Normal file
61
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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_add_no_contraction_decorations.h"
|
||||
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
|
||||
|
||||
FuzzerPassAddNoContractionDecorations::
|
||||
~FuzzerPassAddNoContractionDecorations() = default;
|
||||
|
||||
void FuzzerPassAddNoContractionDecorations::Apply() {
|
||||
// Consider every instruction in every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& inst : block) {
|
||||
// Restrict attention to arithmetic instructions (as defined in the
|
||||
// SPIR-V specification).
|
||||
if (TransformationAddNoContractionDecoration::IsArithmetic(
|
||||
inst.opcode())) {
|
||||
// Randomly choose whether to apply the NoContraction decoration to
|
||||
// this arithmetic instruction.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfAddingNoContractionDecoration())) {
|
||||
TransformationAddNoContractionDecoration transformation(
|
||||
inst.result_id());
|
||||
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
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
vendored
Normal file
39
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
vendored
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_ADD_NO_CONTRACTION_DECORATIONS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that applies the NoContraction decoration to arithmetic instructions.
|
||||
class FuzzerPassAddNoContractionDecorations : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddNoContractionDecorations(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddNoContractionDecorations() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_
|
||||
@@ -24,8 +24,6 @@
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
using opt::IRContext;
|
||||
|
||||
FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
|
||||
73
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
vendored
Normal file
73
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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_function_controls.h"
|
||||
|
||||
#include "source/fuzz/transformation_set_function_control.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
|
||||
|
||||
FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default;
|
||||
|
||||
void FuzzerPassAdjustFunctionControls::Apply() {
|
||||
// Consider every function in the module.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
// Randomly decide whether to adjust this function's controls.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAdjustingFunctionControl())) {
|
||||
// Grab the function control mask for the function in its present form.
|
||||
uint32_t existing_function_control_mask =
|
||||
function.DefInst().GetSingleWordInOperand(0);
|
||||
|
||||
// For the new mask, we first randomly select one of three basic masks:
|
||||
// None, Inline or DontInline. These are always valid (and are mutually
|
||||
// exclusive).
|
||||
std::vector<uint32_t> basic_function_control_masks = {
|
||||
SpvFunctionControlMaskNone, SpvFunctionControlInlineMask,
|
||||
SpvFunctionControlDontInlineMask};
|
||||
uint32_t new_function_control_mask =
|
||||
basic_function_control_masks[GetFuzzerContext()->RandomIndex(
|
||||
basic_function_control_masks)];
|
||||
|
||||
// We now consider the Pure and Const mask bits. If these are already
|
||||
// set on the function then it's OK to keep them, but also interesting
|
||||
// to consider dropping them, so we decide randomly in each case.
|
||||
for (auto mask_bit :
|
||||
{SpvFunctionControlPureMask, SpvFunctionControlConstMask}) {
|
||||
if ((existing_function_control_mask & mask_bit) &&
|
||||
GetFuzzerContext()->ChooseEven()) {
|
||||
new_function_control_mask |= mask_bit;
|
||||
}
|
||||
}
|
||||
|
||||
// Create and add a transformation.
|
||||
TransformationSetFunctionControl transformation(
|
||||
function.DefInst().result_id(), new_function_control_mask);
|
||||
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
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h
vendored
Normal file
39
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h
vendored
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_FUNCTION_CONTROLS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that adjusts the function controls on OpFunction instructions.
|
||||
class FuzzerPassAdjustFunctionControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustFunctionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAdjustFunctionControls() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_
|
||||
121
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
vendored
Normal file
121
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
vendored
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
|
||||
@@ -12,25 +12,28 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
|
||||
#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
|
||||
|
||||
#include "source/reduce/reduction_opportunity_finder.h"
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace fuzz {
|
||||
|
||||
// A finder for opportunities to remove relaxed precision decorations.
|
||||
class RemoveRelaxedPrecisionDecorationOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
// A pass that adjusts the loop controls on OpLoopMerge instructions.
|
||||
class FuzzerPassAdjustLoopControls : public FuzzerPass {
|
||||
public:
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const override;
|
||||
FuzzerPassAdjustLoopControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
std::string GetName() const override;
|
||||
~FuzzerPassAdjustLoopControls() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
|
||||
76
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
vendored
Normal file
76
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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_selection_controls.h"
|
||||
|
||||
#include "source/fuzz/transformation_set_selection_control.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
|
||||
|
||||
FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
|
||||
default;
|
||||
|
||||
void FuzzerPassAdjustSelectionControls::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 selection merge.
|
||||
if (merge_inst->opcode() != SpvOpSelectionMerge) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Choose randomly whether to change the selection control for this
|
||||
// instruction.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The choices to change the selection control to are the set of valid
|
||||
// controls, minus the current control.
|
||||
std::vector<uint32_t> choices;
|
||||
for (auto control :
|
||||
{SpvSelectionControlMaskNone, SpvSelectionControlFlattenMask,
|
||||
SpvSelectionControlDontFlattenMask}) {
|
||||
if (control == merge_inst->GetSingleWordOperand(1)) {
|
||||
continue;
|
||||
}
|
||||
choices.push_back(control);
|
||||
}
|
||||
|
||||
// Apply the transformation and add it to the output transformation
|
||||
// sequence.
|
||||
TransformationSetSelectionControl transformation(
|
||||
block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
|
||||
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
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h
vendored
Normal file
39
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h
vendored
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_SELECTION_CONTROLS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that adjusts the selection controls on OpSelectionMerge instructions.
|
||||
class FuzzerPassAdjustSelectionControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustSelectionControls(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAdjustSelectionControls() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
|
||||
@@ -63,9 +63,6 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
GetFuzzerContext()->RandomIndex(synonyms_to_try);
|
||||
auto synonym_to_try = synonyms_to_try[synonym_index];
|
||||
synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index);
|
||||
assert(synonym_to_try->index().empty() &&
|
||||
"Right now we only support id == id synonyms; supporting "
|
||||
"e.g. id == index-into-vector will come later");
|
||||
|
||||
if (!TransformationReplaceIdWithSynonym::
|
||||
ReplacingUseWithSynonymIsOk(GetIRContext(), use_inst,
|
||||
@@ -74,10 +71,24 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// At present, we generate direct id synonyms (through
|
||||
// OpCopyObject), which require no indices, and id synonyms that
|
||||
// require a single index (through OpCompositeConstruct).
|
||||
assert(synonym_to_try->index_size() <= 1);
|
||||
|
||||
// If an index is required, then we need to extract an element
|
||||
// from a composite (e.g. through OpCompositeExtract), and this
|
||||
// requires a fresh result id.
|
||||
auto fresh_id_for_temporary =
|
||||
synonym_to_try->index().empty()
|
||||
? 0
|
||||
: GetFuzzerContext()->GetFreshId();
|
||||
|
||||
TransformationReplaceIdWithSynonym replace_id_transformation(
|
||||
transformation::MakeIdUseDescriptorFromUse(
|
||||
GetIRContext(), use_inst, use_in_operand_index),
|
||||
*synonym_to_try, 0);
|
||||
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
|
||||
use_in_operand_index),
|
||||
*synonym_to_try, fresh_id_for_temporary);
|
||||
|
||||
// The transformation should be applicable by construction.
|
||||
assert(replace_id_transformation.IsApplicable(GetIRContext(),
|
||||
*GetFactManager()));
|
||||
|
||||
328
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp
vendored
Normal file
328
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
// 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_construct_composites.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_construct_composite.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassConstructComposites::FuzzerPassConstructComposites(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||
|
||||
FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
|
||||
|
||||
void FuzzerPassConstructComposites::Apply() {
|
||||
// Gather up the ids of all composite types.
|
||||
std::vector<uint32_t> composite_type_ids;
|
||||
for (auto& inst : GetIRContext()->types_values()) {
|
||||
if (fuzzerutil::IsCompositeType(
|
||||
GetIRContext()->get_type_mgr()->GetType(inst.result_id()))) {
|
||||
composite_type_ids.push_back(inst.result_id());
|
||||
}
|
||||
}
|
||||
|
||||
MaybeAddTransformationBeforeEachInstruction(
|
||||
[this, &composite_type_ids](const opt::Function& function,
|
||||
opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
uint32_t base, uint32_t offset) -> uint32_t {
|
||||
// Check whether it is legitimate to insert a composite construction
|
||||
// before the instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
|
||||
SpvOpCompositeConstruct, inst_it)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting an object copy here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfConstructingComposite())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For each instruction that is available at this program point (i.e. an
|
||||
// instruction that is global or whose definition strictly dominates the
|
||||
// program point) and suitable for making a synonym of, associate it
|
||||
// with the id of its result type.
|
||||
TypeIdToInstructions type_id_to_available_instructions;
|
||||
for (auto instruction : FindAvailableInstructions(
|
||||
function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
|
||||
RecordAvailableInstruction(instruction,
|
||||
&type_id_to_available_instructions);
|
||||
}
|
||||
|
||||
// At this point, |composite_type_ids| captures all the composite types
|
||||
// we could try to create, while |type_id_to_available_instructions|
|
||||
// captures all the available result ids we might use, organized by
|
||||
// type.
|
||||
|
||||
// Now we try to find a composite that we can construct. We might not
|
||||
// manage, if there is a paucity of available ingredients in the module
|
||||
// (e.g. if our only available composite was a boolean vector and we had
|
||||
// no instructions generating boolean result types available).
|
||||
//
|
||||
// If we succeed, |chosen_composite_type| will end up being non-zero,
|
||||
// and |constructor_arguments| will end up giving us result ids suitable
|
||||
// for constructing a composite of that type. Otherwise these variables
|
||||
// will remain 0 and null respectively.
|
||||
uint32_t chosen_composite_type = 0;
|
||||
std::unique_ptr<std::vector<uint32_t>> constructor_arguments = nullptr;
|
||||
|
||||
// Initially, all composite type ids are available for us to try. Keep
|
||||
// trying until we run out of options.
|
||||
auto composites_to_try_constructing = composite_type_ids;
|
||||
while (!composites_to_try_constructing.empty()) {
|
||||
// Remove a composite type from the composite types left for us to
|
||||
// try.
|
||||
auto index =
|
||||
GetFuzzerContext()->RandomIndex(composites_to_try_constructing);
|
||||
auto next_composite_to_try_constructing =
|
||||
composites_to_try_constructing[index];
|
||||
composites_to_try_constructing.erase(
|
||||
composites_to_try_constructing.begin() + index);
|
||||
|
||||
// Now try to construct a composite of this type, using an appropriate
|
||||
// helper method depending on the kind of composite type.
|
||||
auto composite_type = GetIRContext()->get_type_mgr()->GetType(
|
||||
next_composite_to_try_constructing);
|
||||
if (auto array_type = composite_type->AsArray()) {
|
||||
constructor_arguments = TryConstructingArrayComposite(
|
||||
*array_type, type_id_to_available_instructions);
|
||||
} else if (auto matrix_type = composite_type->AsMatrix()) {
|
||||
constructor_arguments = TryConstructingMatrixComposite(
|
||||
*matrix_type, type_id_to_available_instructions);
|
||||
} else if (auto struct_type = composite_type->AsStruct()) {
|
||||
constructor_arguments = TryConstructingStructComposite(
|
||||
*struct_type, type_id_to_available_instructions);
|
||||
} else {
|
||||
auto vector_type = composite_type->AsVector();
|
||||
assert(vector_type &&
|
||||
"The space of possible composite types should be covered by "
|
||||
"the above cases.");
|
||||
constructor_arguments = TryConstructingVectorComposite(
|
||||
*vector_type, type_id_to_available_instructions);
|
||||
}
|
||||
if (constructor_arguments != nullptr) {
|
||||
// We succeeded! Note the composite type we finally settled on, and
|
||||
// exit from the loop.
|
||||
chosen_composite_type = next_composite_to_try_constructing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!chosen_composite_type) {
|
||||
// We did not manage to make a composite; return 0 to indicate that no
|
||||
// instructions were added.
|
||||
assert(constructor_arguments == nullptr);
|
||||
return 0;
|
||||
}
|
||||
assert(constructor_arguments != nullptr);
|
||||
|
||||
// Make and apply a transformation.
|
||||
TransformationConstructComposite transformation(
|
||||
chosen_composite_type, *constructor_arguments, base, offset,
|
||||
GetFuzzerContext()->GetFreshId());
|
||||
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
"This transformation should be applicable by construction.");
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
*GetTransformations()->add_transformation() =
|
||||
transformation.ToMessage();
|
||||
// Indicate that one instruction was added.
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
void FuzzerPassConstructComposites::RecordAvailableInstruction(
|
||||
opt::Instruction* inst,
|
||||
TypeIdToInstructions* type_id_to_available_instructions) {
|
||||
if (type_id_to_available_instructions->count(inst->type_id()) == 0) {
|
||||
(*type_id_to_available_instructions)[inst->type_id()] = {};
|
||||
}
|
||||
type_id_to_available_instructions->at(inst->type_id()).push_back(inst);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingArrayComposite(
|
||||
const opt::analysis::Array& array_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
// TODO make these be true by construction
|
||||
assert(array_type.length_info().words.size() == 2);
|
||||
assert(array_type.length_info().words[0] ==
|
||||
opt::analysis::Array::LengthInfo::kConstant);
|
||||
|
||||
auto result = MakeUnique<std::vector<uint32_t>>();
|
||||
auto element_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(array_type.element_type());
|
||||
auto available_instructions =
|
||||
type_id_to_available_instructions.find(element_type_id);
|
||||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
// TODO comment infeasible
|
||||
return nullptr;
|
||||
}
|
||||
for (uint32_t index = 0; index < array_type.length_info().words[1]; index++) {
|
||||
result->push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingMatrixComposite(
|
||||
const opt::analysis::Matrix& matrix_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
(void)(matrix_type);
|
||||
(void)(type_id_to_available_instructions);
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingStructComposite(
|
||||
const opt::analysis::Struct& struct_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
auto result = MakeUnique<std::vector<uint32_t>>();
|
||||
for (auto element_type : struct_type.element_types()) {
|
||||
auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
|
||||
auto available_instructions =
|
||||
type_id_to_available_instructions.find(element_type_id);
|
||||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
// TODO comment infeasible
|
||||
return nullptr;
|
||||
}
|
||||
result->push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint32_t>>
|
||||
FuzzerPassConstructComposites::TryConstructingVectorComposite(
|
||||
const opt::analysis::Vector& vector_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
// Get details of the type underlying the vector, and the width of the vector,
|
||||
// for convenience.
|
||||
auto element_type = vector_type.element_type();
|
||||
auto element_count = vector_type.element_count();
|
||||
|
||||
// Collect a mapping, from type id to width, for scalar/vector types that are
|
||||
// smaller in width than |vector_type|, but that have the same underlying
|
||||
// type. For example, if |vector_type| is vec4, the mapping will be { float
|
||||
// -> 1, vec2 -> 2, vec3 -> 3 }. The mapping will have missing entries if
|
||||
// some of these types do not exist.
|
||||
|
||||
// TODO comment why we have the list as well.
|
||||
std::vector<uint32_t> smaller_vector_type_ids;
|
||||
std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
|
||||
// Add the underlying type. This id must exist, in order for |vector_type| to
|
||||
// exist.
|
||||
auto scalar_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
|
||||
smaller_vector_type_ids.push_back(scalar_type_id);
|
||||
smaller_vector_type_id_to_width[scalar_type_id] = 1;
|
||||
|
||||
// Now add every vector type with width at least 2, and less than the width of
|
||||
// |vector_type|.
|
||||
for (uint32_t width = 2; width < element_count; width++) {
|
||||
opt::analysis::Vector smaller_vector_type(vector_type.element_type(),
|
||||
width);
|
||||
auto smaller_vector_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
|
||||
// TODO recap why it might be 0
|
||||
if (smaller_vector_type_id) {
|
||||
smaller_vector_type_ids.push_back(smaller_vector_type_id);
|
||||
smaller_vector_type_id_to_width[smaller_vector_type_id] = width;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we know the types that are available to us, we set about populating a
|
||||
// vector of the right length. We do this by deciding, with no order in mind,
|
||||
// which instructions we will use to populate the vector, and subsequently
|
||||
// randomly choosing an order. This is to avoid biasing construction of
|
||||
// vectors with smaller vectors to the left and scalars to the right. That is
|
||||
// a concern because, e.g. in the case of populating a vec4, if we populate
|
||||
// the constructor instructions left-to-right, we can always choose a vec3 to
|
||||
// construct the first three elements, but can only choose a vec3 to construct
|
||||
// the last three elements if we chose a float to construct the first element
|
||||
// (otherwise there will not be space left for a vec3).
|
||||
|
||||
uint32_t vector_slots_used = 0;
|
||||
// The instructions we will use to construct the vector, in no particular
|
||||
// order at this stage.
|
||||
std::vector<opt::Instruction*> instructions_to_use;
|
||||
|
||||
while (vector_slots_used < vector_type.element_count()) {
|
||||
std::vector<opt::Instruction*> instructions_to_choose_from;
|
||||
for (auto& entry : smaller_vector_type_id_to_width) {
|
||||
if (entry.second >
|
||||
std::min(vector_type.element_count() - 1,
|
||||
vector_type.element_count() - vector_slots_used)) {
|
||||
continue;
|
||||
}
|
||||
auto available_instructions =
|
||||
type_id_to_available_instructions.find(entry.first);
|
||||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
continue;
|
||||
}
|
||||
instructions_to_choose_from.insert(instructions_to_choose_from.end(),
|
||||
available_instructions->second.begin(),
|
||||
available_instructions->second.end());
|
||||
}
|
||||
if (instructions_to_choose_from.empty()) {
|
||||
// TODO comment - like fuzzed into a corner
|
||||
return nullptr;
|
||||
}
|
||||
auto instruction_to_use =
|
||||
instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
|
||||
instructions_to_choose_from)];
|
||||
instructions_to_use.push_back(instruction_to_use);
|
||||
auto chosen_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id());
|
||||
if (chosen_type->AsVector()) {
|
||||
assert(chosen_type->AsVector()->element_type() == element_type);
|
||||
assert(chosen_type->AsVector()->element_count() < element_count);
|
||||
assert(chosen_type->AsVector()->element_count() <=
|
||||
element_count - vector_slots_used);
|
||||
vector_slots_used += chosen_type->AsVector()->element_count();
|
||||
} else {
|
||||
assert(chosen_type == element_type);
|
||||
vector_slots_used += 1;
|
||||
}
|
||||
}
|
||||
assert(vector_slots_used == vector_type.element_count());
|
||||
|
||||
auto result = MakeUnique<std::vector<uint32_t>>();
|
||||
std::vector<uint32_t> operands;
|
||||
while (!instructions_to_use.empty()) {
|
||||
auto index = GetFuzzerContext()->RandomIndex(instructions_to_use);
|
||||
result->push_back(instructions_to_use[index]->result_id());
|
||||
instructions_to_use.erase(instructions_to_use.begin() + index);
|
||||
}
|
||||
assert(result->size() > 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
79
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h
vendored
Normal file
79
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h
vendored
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_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass for constructing composite objects from smaller objects.
|
||||
class FuzzerPassConstructComposites : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassConstructComposites(
|
||||
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassConstructComposites();
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Used to map a type id to relevant instructions whose result type matches
|
||||
// the type id.
|
||||
typedef std::map<uint32_t, std::vector<opt::Instruction*>>
|
||||
TypeIdToInstructions;
|
||||
|
||||
// Considers all instructions that are available at |inst| - instructions
|
||||
// whose results could be packed into a composite - and updates
|
||||
// |type_id_to_available_instructions| so that each such instruction is
|
||||
// associated with its the id of its result type.
|
||||
void RecordAvailableInstruction(
|
||||
opt::Instruction* inst,
|
||||
TypeIdToInstructions* type_id_to_available_instructions);
|
||||
|
||||
// Attempts to find suitable instruction result ids from the values of
|
||||
// |type_id_to_available_instructions| that would allow a composite of type
|
||||
// |array_type| to be constructed. Returns said ids if they can be found.
|
||||
// Returns |nullptr| otherwise.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingArrayComposite(
|
||||
const opt::analysis::Array& array_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to TryConstructingArrayComposite, but for matrices.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingMatrixComposite(
|
||||
const opt::analysis::Matrix& matrix_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to TryConstructingArrayComposite, but for structs.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingStructComposite(
|
||||
const opt::analysis::Struct& struct_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to TryConstructingArrayComposite, but for vectors.
|
||||
std::unique_ptr<std::vector<uint32_t>> TryConstructingVectorComposite(
|
||||
const opt::analysis::Vector& vector_type,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_copy_objects.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_copy_object.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -28,120 +29,46 @@ FuzzerPassCopyObjects::FuzzerPassCopyObjects(
|
||||
FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
|
||||
|
||||
void FuzzerPassCopyObjects::Apply() {
|
||||
// Consider every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// We now consider every instruction in the block, randomly deciding
|
||||
// whether to add an object copy before the instruction.
|
||||
|
||||
// In order to insert an object copy instruction, we need to be able to
|
||||
// identify the instruction a copy should be inserted before. We do this
|
||||
// by tracking a base instruction, which must generate a result id, and an
|
||||
// offset (to allow us to identify instructions that do not generate
|
||||
// result ids).
|
||||
|
||||
// The initial base instruction is the block label.
|
||||
uint32_t base = block.id();
|
||||
uint32_t offset = 0;
|
||||
// Consider every instruction in the block.
|
||||
for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
|
||||
if (inst_it->HasResultId()) {
|
||||
// In the case that the instruction has a result id, we use the
|
||||
// instruction as its own base, with zero offset.
|
||||
base = inst_it->result_id();
|
||||
offset = 0;
|
||||
} else {
|
||||
// The instruction does not have a result id, so we need to identify
|
||||
// it via the latest instruction that did have a result id (base), and
|
||||
// an incremented offset.
|
||||
offset++;
|
||||
}
|
||||
|
||||
MaybeAddTransformationBeforeEachInstruction(
|
||||
[this](const opt::Function& function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it, uint32_t base,
|
||||
uint32_t offset) -> uint32_t {
|
||||
// Check whether it is legitimate to insert a copy before this
|
||||
// instruction.
|
||||
if (!TransformationCopyObject::CanInsertCopyBefore(inst_it)) {
|
||||
continue;
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
|
||||
inst_it)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting an object copy here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfCopyingObject())) {
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Populate list of potential instructions that can be copied.
|
||||
// TODO(afd) The following is (relatively) simple, but may end up being
|
||||
// prohibitively inefficient, as it walks the whole dominator tree for
|
||||
// every copy that is added.
|
||||
std::vector<opt::Instruction*> copyable_instructions;
|
||||
std::vector<opt::Instruction*> relevant_instructions =
|
||||
FindAvailableInstructions(function, block, inst_it,
|
||||
fuzzerutil::CanMakeSynonymOf);
|
||||
|
||||
// Consider all global declarations
|
||||
for (auto& global : GetIRContext()->module()->types_values()) {
|
||||
if (TransformationCopyObject::IsCopyable(GetIRContext(), &global)) {
|
||||
copyable_instructions.push_back(&global);
|
||||
}
|
||||
}
|
||||
|
||||
// Consider all previous instructions in this block
|
||||
for (auto prev_inst_it = block.begin(); prev_inst_it != inst_it;
|
||||
++prev_inst_it) {
|
||||
if (TransformationCopyObject::IsCopyable(GetIRContext(),
|
||||
&*prev_inst_it)) {
|
||||
copyable_instructions.push_back(&*prev_inst_it);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the dominator tree to consider all instructions from dominating
|
||||
// blocks
|
||||
auto dominator_analysis =
|
||||
GetIRContext()->GetDominatorAnalysis(&function);
|
||||
for (auto next_dominator =
|
||||
dominator_analysis->ImmediateDominator(&block);
|
||||
next_dominator != nullptr;
|
||||
next_dominator =
|
||||
dominator_analysis->ImmediateDominator(next_dominator)) {
|
||||
for (auto& dominating_inst : *next_dominator) {
|
||||
if (TransformationCopyObject::IsCopyable(GetIRContext(),
|
||||
&dominating_inst)) {
|
||||
copyable_instructions.push_back(&dominating_inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, |copyable_instructions| contains all the instructions
|
||||
// At this point, |relevant_instructions| contains all the instructions
|
||||
// we might think of copying.
|
||||
|
||||
if (!copyable_instructions.empty()) {
|
||||
// Choose a copyable instruction at random, and create and apply an
|
||||
// object copying transformation based on it.
|
||||
uint32_t index =
|
||||
GetFuzzerContext()->RandomIndex(copyable_instructions);
|
||||
TransformationCopyObject transformation(
|
||||
copyable_instructions[index]->result_id(), base, offset,
|
||||
GetFuzzerContext()->GetFreshId());
|
||||
assert(
|
||||
transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
"This transformation should be applicable by construction.");
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
*GetTransformations()->add_transformation() =
|
||||
transformation.ToMessage();
|
||||
|
||||
if (!inst_it->HasResultId()) {
|
||||
// We have inserted a new instruction before the current
|
||||
// instruction, and we are tracking the current id-less instruction
|
||||
// via an offset (offset) from a previous instruction (base) that
|
||||
// has an id. We increment |offset| to reflect the newly-inserted
|
||||
// instruction.
|
||||
//
|
||||
// This is slightly preferable to the alternative of setting |base|
|
||||
// to be the result id of the new instruction, since on replay we
|
||||
// might end up eliminating this copy but keeping a subsequent copy.
|
||||
offset++;
|
||||
}
|
||||
if (relevant_instructions.empty()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Choose a copyable instruction at random, and create and apply an
|
||||
// object copying transformation based on it.
|
||||
uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions);
|
||||
TransformationCopyObject transformation(
|
||||
relevant_instructions[index]->result_id(), base, offset,
|
||||
GetFuzzerContext()->GetFreshId());
|
||||
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||
"This transformation should be applicable by construction.");
|
||||
transformation.Apply(GetIRContext(), GetFactManager());
|
||||
*GetTransformations()->add_transformation() =
|
||||
transformation.ToMessage();
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
@@ -102,10 +103,11 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
|
||||
// We randomly decide, based on the current depth of obfuscation, whether
|
||||
// to further obfuscate this operand.
|
||||
if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) {
|
||||
auto in_operand_use = transformation::MakeIdUseDescriptor(
|
||||
auto in_operand_use = MakeIdUseDescriptor(
|
||||
binary_operator_instruction->GetSingleWordInOperand(index),
|
||||
binary_operator_instruction->opcode(), index,
|
||||
binary_operator_instruction->result_id(), 0);
|
||||
MakeInstructionDescriptor(binary_operator_instruction->result_id(),
|
||||
binary_operator_instruction->opcode(), 0),
|
||||
index);
|
||||
ObfuscateConstant(depth + 1, in_operand_use);
|
||||
}
|
||||
}
|
||||
@@ -366,14 +368,17 @@ void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
|
||||
// it.
|
||||
protobufs::IdUseDescriptor id_use_descriptor;
|
||||
id_use_descriptor.set_id_of_interest(operand_id);
|
||||
id_use_descriptor.set_target_instruction_opcode(inst.opcode());
|
||||
id_use_descriptor.mutable_enclosing_instruction()
|
||||
->set_target_instruction_opcode(inst.opcode());
|
||||
id_use_descriptor.mutable_enclosing_instruction()
|
||||
->set_base_instruction_result_id(base_instruction_result_id);
|
||||
id_use_descriptor.mutable_enclosing_instruction()
|
||||
->set_num_opcodes_to_ignore(
|
||||
skipped_opcode_count.find(inst.opcode()) ==
|
||||
skipped_opcode_count.end()
|
||||
? 0
|
||||
: skipped_opcode_count.at(inst.opcode()));
|
||||
id_use_descriptor.set_in_operand_index(in_operand_index);
|
||||
id_use_descriptor.set_base_instruction_result_id(
|
||||
base_instruction_result_id);
|
||||
id_use_descriptor.set_num_opcodes_to_ignore(
|
||||
skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end()
|
||||
? 0
|
||||
: skipped_opcode_count.at(inst.opcode()));
|
||||
constant_uses->push_back(id_use_descriptor);
|
||||
} break;
|
||||
default:
|
||||
|
||||
48
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
48
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@@ -216,6 +216,13 @@ bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): the
|
||||
// solution below to determining whether a new edge respects dominance
|
||||
// rules is incomplete. Test
|
||||
// TransformationAddDeadContinueTest::DISABLED_Miscellaneous6 exposes the
|
||||
// problem. In practice, this limitation does not bite too often, and the
|
||||
// worst it does is leads to SPIR-V that spirv-val rejects.
|
||||
|
||||
// Let us assume that the module being manipulated is valid according to the
|
||||
// rules of the SPIR-V language.
|
||||
//
|
||||
@@ -307,6 +314,47 @@ bool BlockIsReachableInItsFunction(opt::IRContext* context,
|
||||
->Dominates(enclosing_function->entry().get(), bb);
|
||||
}
|
||||
|
||||
bool CanInsertOpcodeBeforeInstruction(
|
||||
SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
|
||||
if (instruction_in_block->PreviousNode() &&
|
||||
(instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
|
||||
instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
|
||||
// We cannot insert directly after a merge instruction.
|
||||
return false;
|
||||
}
|
||||
if (opcode != SpvOpVariable &&
|
||||
instruction_in_block->opcode() == SpvOpVariable) {
|
||||
// We cannot insert a non-OpVariable instruction directly before a
|
||||
// variable; variables in a function must be contiguous in the entry block.
|
||||
return false;
|
||||
}
|
||||
// We cannot insert a non-OpPhi instruction directly before an OpPhi, because
|
||||
// OpPhi instructions need to be contiguous at the start of a block.
|
||||
return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
|
||||
}
|
||||
|
||||
bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
if (!inst->HasResultId()) {
|
||||
// We can only make a synonym of an instruction that generates an id.
|
||||
return false;
|
||||
}
|
||||
if (!inst->type_id()) {
|
||||
// We can only make a synonym of an instruction that has a type.
|
||||
return false;
|
||||
}
|
||||
// We do not make synonyms of objects that have decorations: if the synonym is
|
||||
// not decorated analogously, using the original object vs. its synonymous
|
||||
// form may not be equivalent.
|
||||
return ir_context->get_decoration_mgr()
|
||||
->GetDecorationsFor(inst->result_id(), true)
|
||||
.empty();
|
||||
}
|
||||
|
||||
bool IsCompositeType(const opt::analysis::Type* type) {
|
||||
return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() ||
|
||||
type->AsVector());
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
12
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
12
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
@@ -87,6 +87,18 @@ bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
|
||||
bool BlockIsReachableInItsFunction(opt::IRContext* context,
|
||||
opt::BasicBlock* bb);
|
||||
|
||||
// Determines whether it is OK to insert an instruction with opcode |opcode|
|
||||
// before |instruction_in_block|.
|
||||
bool CanInsertOpcodeBeforeInstruction(
|
||||
SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
|
||||
|
||||
// Determines whether it is OK to make a synonym of |inst|.
|
||||
bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst);
|
||||
|
||||
// Determines whether the given type is a composite; that is: an array, matrix,
|
||||
// struct or vector.
|
||||
bool IsCompositeType(const opt::analysis::Type* type);
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
@@ -14,71 +14,41 @@
|
||||
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
opt::Instruction* transformation::FindInstruction(
|
||||
const protobufs::IdUseDescriptor& descriptor,
|
||||
spvtools::opt::IRContext* context) {
|
||||
for (auto& function : *context->module()) {
|
||||
for (auto& block : function) {
|
||||
bool found_base = block.id() == descriptor.base_instruction_result_id();
|
||||
uint32_t num_ignored = 0;
|
||||
for (auto& instruction : block) {
|
||||
if (instruction.HasResultId() &&
|
||||
instruction.result_id() ==
|
||||
descriptor.base_instruction_result_id()) {
|
||||
assert(!found_base &&
|
||||
"It should not be possible to find the base instruction "
|
||||
"multiple times.");
|
||||
found_base = true;
|
||||
assert(num_ignored == 0 &&
|
||||
"The skipped instruction count should only be incremented "
|
||||
"after the instruction base has been found.");
|
||||
}
|
||||
if (found_base &&
|
||||
instruction.opcode() == descriptor.target_instruction_opcode()) {
|
||||
if (num_ignored == descriptor.num_opcodes_to_ignore()) {
|
||||
if (descriptor.in_operand_index() >= instruction.NumInOperands()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto in_operand =
|
||||
instruction.GetInOperand(descriptor.in_operand_index());
|
||||
if (in_operand.type != SPV_OPERAND_TYPE_ID) {
|
||||
return nullptr;
|
||||
}
|
||||
if (in_operand.words[0] != descriptor.id_of_interest()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &instruction;
|
||||
}
|
||||
num_ignored++;
|
||||
}
|
||||
}
|
||||
if (found_base) {
|
||||
// We found the base instruction, but did not find the target
|
||||
// instruction in the same block.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
opt::Instruction* FindInstructionContainingUse(
|
||||
const protobufs::IdUseDescriptor& id_use_descriptor,
|
||||
opt::IRContext* context) {
|
||||
auto result =
|
||||
FindInstruction(id_use_descriptor.enclosing_instruction(), context);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
if (id_use_descriptor.in_operand_index() >= result->NumInOperands()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (result->GetSingleWordInOperand(id_use_descriptor.in_operand_index()) !=
|
||||
id_use_descriptor.id_of_interest()) {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protobufs::IdUseDescriptor transformation::MakeIdUseDescriptor(
|
||||
uint32_t id_of_interest, SpvOp target_instruction_opcode,
|
||||
uint32_t in_operand_index, uint32_t base_instruction_result_id,
|
||||
uint32_t num_opcodes_to_ignore) {
|
||||
protobufs::IdUseDescriptor result;
|
||||
result.set_id_of_interest(id_of_interest);
|
||||
result.set_target_instruction_opcode(target_instruction_opcode);
|
||||
result.set_in_operand_index(in_operand_index);
|
||||
result.set_base_instruction_result_id(base_instruction_result_id);
|
||||
result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
|
||||
return result;
|
||||
}
|
||||
|
||||
protobufs::IdUseDescriptor transformation::MakeIdUseDescriptorFromUse(
|
||||
protobufs::IdUseDescriptor MakeIdUseDescriptor(
|
||||
uint32_t id_of_interest,
|
||||
const protobufs::InstructionDescriptor& enclosing_instruction,
|
||||
uint32_t in_operand_index) {
|
||||
protobufs::IdUseDescriptor result;
|
||||
result.set_id_of_interest(id_of_interest);
|
||||
*result.mutable_enclosing_instruction() = enclosing_instruction;
|
||||
result.set_in_operand_index(in_operand_index);
|
||||
return result;
|
||||
}
|
||||
|
||||
protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
|
||||
opt::IRContext* context, opt::Instruction* inst,
|
||||
uint32_t in_operand_index) {
|
||||
auto in_operand = inst->GetInOperand(in_operand_index);
|
||||
@@ -94,9 +64,11 @@ protobufs::IdUseDescriptor transformation::MakeIdUseDescriptorFromUse(
|
||||
num_opcodes_to_ignore = 0;
|
||||
}
|
||||
if (&inst_in_block == inst) {
|
||||
return MakeIdUseDescriptor(id_of_interest, inst->opcode(),
|
||||
in_operand_index, base_instruction_result_id,
|
||||
num_opcodes_to_ignore);
|
||||
return MakeIdUseDescriptor(
|
||||
id_of_interest,
|
||||
MakeInstructionDescriptor(base_instruction_result_id, inst->opcode(),
|
||||
num_opcodes_to_ignore),
|
||||
in_operand_index);
|
||||
}
|
||||
if (inst_in_block.opcode() == inst->opcode()) {
|
||||
num_opcodes_to_ignore++;
|
||||
|
||||
@@ -12,28 +12,28 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_ID_USE_LOCATOR_H_
|
||||
#define SOURCE_FUZZ_ID_USE_LOCATOR_H_
|
||||
#ifndef SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
|
||||
#define SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace transformation {
|
||||
|
||||
// Looks for an instruction in |context| such that the id use represented by
|
||||
// |descriptor| is one of the operands to said instruction. Returns |nullptr|
|
||||
// if no such instruction can be found.
|
||||
opt::Instruction* FindInstruction(const protobufs::IdUseDescriptor& descriptor,
|
||||
opt::IRContext* context);
|
||||
// Looks for an instruction in |context| that contains a use
|
||||
// identified by |id_use_descriptor|.
|
||||
// Returns |nullptr| if no such instruction can be found.
|
||||
opt::Instruction* FindInstructionContainingUse(
|
||||
const protobufs::IdUseDescriptor& id_use_descriptor,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Creates an IdUseDescriptor protobuf message from the given components.
|
||||
// See the protobuf definition for details of what these components mean.
|
||||
protobufs::IdUseDescriptor MakeIdUseDescriptor(
|
||||
uint32_t id_of_interest, SpvOp target_instruction_opcode,
|
||||
uint32_t in_operand_index, uint32_t base_instruction_result_id,
|
||||
uint32_t num_opcodes_to_ignore);
|
||||
uint32_t id_of_interest,
|
||||
const protobufs::InstructionDescriptor& enclosing_instruction,
|
||||
uint32_t in_operand_index);
|
||||
|
||||
// Given an id use, represented by the instruction |inst| that uses the id, and
|
||||
// the input operand index |in_operand_index| associated with the usage, returns
|
||||
@@ -41,8 +41,7 @@ protobufs::IdUseDescriptor MakeIdUseDescriptor(
|
||||
protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
|
||||
opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index);
|
||||
|
||||
} // namespace transformation
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_ID_USE_LOCATOR_H_
|
||||
#endif // SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
|
||||
|
||||
70
3rdparty/spirv-tools/source/fuzz/instruction_descriptor.cpp
vendored
Normal file
70
3rdparty/spirv-tools/source/fuzz/instruction_descriptor.cpp
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
opt::Instruction* FindInstruction(
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor,
|
||||
spvtools::opt::IRContext* context) {
|
||||
for (auto& function : *context->module()) {
|
||||
for (auto& block : function) {
|
||||
bool found_base =
|
||||
block.id() == instruction_descriptor.base_instruction_result_id();
|
||||
uint32_t num_ignored = 0;
|
||||
for (auto& instruction : block) {
|
||||
if (instruction.HasResultId() &&
|
||||
instruction.result_id() ==
|
||||
instruction_descriptor.base_instruction_result_id()) {
|
||||
assert(!found_base &&
|
||||
"It should not be possible to find the base instruction "
|
||||
"multiple times.");
|
||||
found_base = true;
|
||||
assert(num_ignored == 0 &&
|
||||
"The skipped instruction count should only be incremented "
|
||||
"after the instruction base has been found.");
|
||||
}
|
||||
if (found_base &&
|
||||
instruction.opcode() ==
|
||||
instruction_descriptor.target_instruction_opcode()) {
|
||||
if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) {
|
||||
return &instruction;
|
||||
}
|
||||
num_ignored++;
|
||||
}
|
||||
}
|
||||
if (found_base) {
|
||||
// We found the base instruction, but did not find the target
|
||||
// instruction in the same block.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protobufs::InstructionDescriptor MakeInstructionDescriptor(
|
||||
uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
|
||||
uint32_t num_opcodes_to_ignore) {
|
||||
protobufs::InstructionDescriptor result;
|
||||
result.set_base_instruction_result_id(base_instruction_result_id);
|
||||
result.set_target_instruction_opcode(target_instruction_opcode);
|
||||
result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
40
3rdparty/spirv-tools/source/fuzz/instruction_descriptor.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/instruction_descriptor.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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_INSTRUCTION_DESCRIPTOR_H_
|
||||
#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Looks for an instruction in |context| corresponding to |descriptor|.
|
||||
// Returns |nullptr| if no such instruction can be found.
|
||||
opt::Instruction* FindInstruction(
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Creates an InstructionDescriptor protobuf message from the given
|
||||
// components. See the protobuf definition for details of what these
|
||||
// components mean.
|
||||
protobufs::InstructionDescriptor MakeInstructionDescriptor(
|
||||
uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
|
||||
uint32_t num_opcodes_to_ignore);
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
|
||||
@@ -21,17 +21,37 @@ syntax = "proto3";
|
||||
|
||||
package spvtools.fuzz.protobufs;
|
||||
|
||||
message InstructionDescriptor {
|
||||
|
||||
// Describes an instruction in some block of a function with respect to a
|
||||
// base instruction.
|
||||
|
||||
// The id of an instruction after which the instruction being described is
|
||||
// believed to be located. It might be the using instruction itself.
|
||||
uint32 base_instruction_result_id = 1;
|
||||
|
||||
// The opcode for the instruction being described.
|
||||
uint32 target_instruction_opcode = 2;
|
||||
|
||||
// The number of matching opcodes to skip over when searching from the base
|
||||
// instruction to the instruction being described.
|
||||
uint32 num_opcodes_to_ignore = 3;
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
// - target_instruction_opcode = OpStore
|
||||
// - enclosing_instruction = (
|
||||
// base_instruction_result_id = 50,
|
||||
// target_instruction_opcode = OpStore
|
||||
// num_opcodes_to_ignore = 7
|
||||
// )
|
||||
// - in_operand_index = 1
|
||||
// - base_instruction_result_id = 50
|
||||
// - num_opcodes_to_ignore = 7
|
||||
// represents a use of id 42 as input operand 1 to an OpStore instruction,
|
||||
// such that the OpStore instruction can be found in the same basic block as
|
||||
// the instruction with result id 50, and in particular is the 8th OpStore
|
||||
@@ -41,20 +61,11 @@ message IdUseDescriptor {
|
||||
// An id that we would like to be able to find a use of.
|
||||
uint32 id_of_interest = 1;
|
||||
|
||||
// The opcode for the instruction that uses the id.
|
||||
uint32 target_instruction_opcode = 2;
|
||||
|
||||
// The input operand index at which the use is expected.
|
||||
InstructionDescriptor enclosing_instruction = 2;
|
||||
|
||||
uint32 in_operand_index = 3;
|
||||
|
||||
// The id of an instruction after which the instruction that contains the use
|
||||
// is believed to occur; it might be the using instruction itself.
|
||||
uint32 base_instruction_result_id = 4;
|
||||
|
||||
// The number of matching opcodes to skip over when searching for the using
|
||||
// instruction from the base instruction.
|
||||
uint32 num_opcodes_to_ignore = 5;
|
||||
|
||||
}
|
||||
|
||||
message DataDescriptor {
|
||||
@@ -167,12 +178,18 @@ 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;
|
||||
TransformationCopyObject copy_object = 13;
|
||||
TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
|
||||
TransformationSetSelectionControl set_selection_control = 15;
|
||||
TransformationConstructComposite construct_composite = 16;
|
||||
TransformationSetLoopControl set_loop_control = 17;
|
||||
TransformationSetFunctionControl set_function_control = 18;
|
||||
TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@@ -245,6 +262,15 @@ message TransformationAddDeadContinue {
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddNoContractionDecoration {
|
||||
|
||||
// Applies OpDecorate NoContraction to the given result id
|
||||
|
||||
// Result id to be decorated
|
||||
uint32 result_id = 1;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddTypeBoolean {
|
||||
|
||||
// Adds OpTypeBool to the module
|
||||
@@ -317,6 +343,30 @@ message TransformationCopyObject {
|
||||
|
||||
}
|
||||
|
||||
message TransformationConstructComposite {
|
||||
|
||||
// A transformation that introduces an OpCompositeConstruct instruction to
|
||||
// make a composite object.
|
||||
|
||||
// Id of the type of the composite that is to be constructed
|
||||
uint32 composite_type_id = 1;
|
||||
|
||||
// Ids of the objects that will form the components of the composite
|
||||
repeated uint32 component = 2;
|
||||
|
||||
// The id of an instruction in a block
|
||||
uint32 base_instruction_id = 3;
|
||||
|
||||
// An offset, such that OpCompositeConstruct instruction should be inserted
|
||||
// right before the instruction |offset| instructions after
|
||||
// |base_instruction_id|
|
||||
uint32 offset = 4;
|
||||
|
||||
// A fresh id for the composite object
|
||||
uint32 fresh_id = 5;
|
||||
|
||||
}
|
||||
|
||||
message TransformationMoveBlockDown {
|
||||
|
||||
// A transformation that moves a basic block to be one position lower in
|
||||
@@ -384,6 +434,59 @@ message TransformationReplaceIdWithSynonym {
|
||||
uint32 fresh_id_for_temporary = 3;
|
||||
}
|
||||
|
||||
message TransformationSetFunctionControl {
|
||||
|
||||
// A transformation that sets the function control operand of an OpFunction
|
||||
// instruction.
|
||||
|
||||
// The result id of an OpFunction instruction
|
||||
uint32 function_id = 1;
|
||||
|
||||
// The value to which the 'function control' operand should be set.
|
||||
uint32 function_control = 2;
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
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 and is not allowed here.
|
||||
uint32 selection_control = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationSplitBlock {
|
||||
|
||||
// A transformation that splits a basic block into two basic blocks
|
||||
|
||||
@@ -16,21 +16,26 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "source/fuzz/transformation_add_constant_boolean.h"
|
||||
#include "source/fuzz/transformation_add_constant_scalar.h"
|
||||
#include "source/fuzz/transformation_add_dead_break.h"
|
||||
#include "source/fuzz/transformation_add_dead_continue.h"
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
#include "source/fuzz/transformation_add_type_float.h"
|
||||
#include "source/fuzz/transformation_add_type_int.h"
|
||||
#include "source/fuzz/transformation_add_type_pointer.h"
|
||||
#include "source/fuzz/transformation_construct_composite.h"
|
||||
#include "source/fuzz/transformation_copy_object.h"
|
||||
#include "source/fuzz/transformation_move_block_down.h"
|
||||
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
#include "source/fuzz/transformation_set_function_control.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"
|
||||
#include "transformation_add_constant_boolean.h"
|
||||
#include "transformation_add_constant_scalar.h"
|
||||
#include "transformation_add_dead_break.h"
|
||||
#include "transformation_add_dead_continue.h"
|
||||
#include "transformation_add_type_boolean.h"
|
||||
#include "transformation_add_type_float.h"
|
||||
#include "transformation_add_type_int.h"
|
||||
#include "transformation_add_type_pointer.h"
|
||||
#include "transformation_copy_object.h"
|
||||
#include "transformation_move_block_down.h"
|
||||
#include "transformation_replace_boolean_constant_with_constant_binary.h"
|
||||
#include "transformation_replace_constant_with_uniform.h"
|
||||
#include "transformation_replace_id_with_synonym.h"
|
||||
#include "transformation_split_block.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@@ -51,6 +56,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
case protobufs::Transformation::TransformationCase::kAddDeadContinue:
|
||||
return MakeUnique<TransformationAddDeadContinue>(
|
||||
message.add_dead_continue());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kAddNoContractionDecoration:
|
||||
return MakeUnique<TransformationAddNoContractionDecoration>(
|
||||
message.add_no_contraction_decoration());
|
||||
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
|
||||
return MakeUnique<TransformationAddTypeBoolean>(
|
||||
message.add_type_boolean());
|
||||
@@ -61,6 +70,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
case protobufs::Transformation::TransformationCase::kAddTypePointer:
|
||||
return MakeUnique<TransformationAddTypePointer>(
|
||||
message.add_type_pointer());
|
||||
case protobufs::Transformation::TransformationCase::kConstructComposite:
|
||||
return MakeUnique<TransformationConstructComposite>(
|
||||
message.construct_composite());
|
||||
case protobufs::Transformation::TransformationCase::kCopyObject:
|
||||
return MakeUnique<TransformationCopyObject>(message.copy_object());
|
||||
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
|
||||
@@ -76,6 +88,15 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
|
||||
return MakeUnique<TransformationReplaceIdWithSynonym>(
|
||||
message.replace_id_with_synonym());
|
||||
case protobufs::Transformation::TransformationCase::kSetFunctionControl:
|
||||
return MakeUnique<TransformationSetFunctionControl>(
|
||||
message.set_function_control());
|
||||
case protobufs::Transformation::TransformationCase::kSetLoopControl:
|
||||
return MakeUnique<TransformationSetLoopControl>(
|
||||
message.set_loop_control());
|
||||
case protobufs::Transformation::TransformationCase::kSetSelectionControl:
|
||||
return MakeUnique<TransformationSetSelectionControl>(
|
||||
message.set_selection_control());
|
||||
case protobufs::Transformation::TransformationCase::kSplitBlock:
|
||||
return MakeUnique<TransformationSplitBlock>(message.split_block());
|
||||
case protobufs::Transformation::TRANSFORMATION_NOT_SET:
|
||||
|
||||
110
3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp
vendored
Normal file
110
3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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_add_no_contraction_decoration.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationAddNoContractionDecoration::
|
||||
TransformationAddNoContractionDecoration(
|
||||
const spvtools::fuzz::protobufs::
|
||||
TransformationAddNoContractionDecoration& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddNoContractionDecoration::
|
||||
TransformationAddNoContractionDecoration(uint32_t result_id) {
|
||||
message_.set_result_id(result_id);
|
||||
}
|
||||
|
||||
bool TransformationAddNoContractionDecoration::IsApplicable(
|
||||
opt::IRContext* context,
|
||||
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||
// |message_.result_id| must be the id of an instruction.
|
||||
auto instr = context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
if (!instr) {
|
||||
return false;
|
||||
}
|
||||
// The instruction must be arithmetic.
|
||||
return IsArithmetic(instr->opcode());
|
||||
}
|
||||
|
||||
void TransformationAddNoContractionDecoration::Apply(
|
||||
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
|
||||
// Add a NoContraction decoration targeting |message_.result_id|.
|
||||
context->get_decoration_mgr()->AddDecoration(message_.result_id(),
|
||||
SpvDecorationNoContraction);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage()
|
||||
const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_no_contraction_decoration() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationAddNoContractionDecoration::IsArithmetic(uint32_t opcode) {
|
||||
switch (opcode) {
|
||||
case SpvOpSNegate:
|
||||
case SpvOpFNegate:
|
||||
case SpvOpIAdd:
|
||||
case SpvOpFAdd:
|
||||
case SpvOpISub:
|
||||
case SpvOpFSub:
|
||||
case SpvOpIMul:
|
||||
case SpvOpFMul:
|
||||
case SpvOpUDiv:
|
||||
case SpvOpSDiv:
|
||||
case SpvOpFDiv:
|
||||
case SpvOpUMod:
|
||||
case SpvOpSRem:
|
||||
case SpvOpSMod:
|
||||
case SpvOpFRem:
|
||||
case SpvOpFMod:
|
||||
case SpvOpVectorTimesScalar:
|
||||
case SpvOpMatrixTimesScalar:
|
||||
case SpvOpVectorTimesMatrix:
|
||||
case SpvOpMatrixTimesVector:
|
||||
case SpvOpMatrixTimesMatrix:
|
||||
case SpvOpOuterProduct:
|
||||
case SpvOpDot:
|
||||
case SpvOpIAddCarry:
|
||||
case SpvOpISubBorrow:
|
||||
case SpvOpUMulExtended:
|
||||
case SpvOpSMulExtended:
|
||||
case SpvOpAny:
|
||||
case SpvOpAll:
|
||||
case SpvOpIsNan:
|
||||
case SpvOpIsInf:
|
||||
case SpvOpIsFinite:
|
||||
case SpvOpIsNormal:
|
||||
case SpvOpSignBitSet:
|
||||
case SpvOpLessOrGreater:
|
||||
case SpvOpOrdered:
|
||||
case SpvOpUnordered:
|
||||
case SpvOpLogicalEqual:
|
||||
case SpvOpLogicalNotEqual:
|
||||
case SpvOpLogicalOr:
|
||||
case SpvOpLogicalAnd:
|
||||
case SpvOpLogicalNot:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
58
3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h
vendored
Normal file
58
3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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_ADD_NO_CONTRACTION_DECORATION_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_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 TransformationAddNoContractionDecoration : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddNoContractionDecoration(
|
||||
const protobufs::TransformationAddNoContractionDecoration& message);
|
||||
|
||||
explicit TransformationAddNoContractionDecoration(uint32_t fresh_id);
|
||||
|
||||
// - |message_.result_id| must be the result id of an arithmetic instruction,
|
||||
// as defined by the SPIR-V specification.
|
||||
// - It does not matter whether this instruction is already annotated with the
|
||||
// NoContraction decoration.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
|
||||
// Adds a decoration of the form:
|
||||
// 'OpDecoration |message_.result_id| NoContraction'
|
||||
// to the module.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Returns true if and only if |opcode| is the opcode of an arithmetic
|
||||
// instruction, as defined by the SPIR-V specification.
|
||||
static bool IsArithmetic(uint32_t opcode);
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddNoContractionDecoration message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
|
||||
310
3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp
vendored
Normal file
310
3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
// 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_construct_composite.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationConstructComposite::TransformationConstructComposite(
|
||||
const protobufs::TransformationConstructComposite& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationConstructComposite::TransformationConstructComposite(
|
||||
uint32_t composite_type_id, std::vector<uint32_t> component,
|
||||
uint32_t base_instruction_id, uint32_t offset, uint32_t fresh_id) {
|
||||
message_.set_composite_type_id(composite_type_id);
|
||||
for (auto a_component : component) {
|
||||
message_.add_component(a_component);
|
||||
}
|
||||
message_.set_base_instruction_id(base_instruction_id);
|
||||
message_.set_offset(offset);
|
||||
message_.set_fresh_id(fresh_id);
|
||||
}
|
||||
|
||||
bool TransformationConstructComposite::IsApplicable(
|
||||
opt::IRContext* context, const FactManager& /*fact_manager*/) const {
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
|
||||
// We require the id for the composite constructor to be unused.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto base_instruction =
|
||||
context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
|
||||
if (!base_instruction) {
|
||||
// The given id to insert after is not defined.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto destination_block = context->get_instr_block(base_instruction);
|
||||
if (!destination_block) {
|
||||
// The given id to insert after is not in a block.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
|
||||
destination_block, base_instruction, message_.offset());
|
||||
|
||||
if (insert_before == destination_block->end()) {
|
||||
// The offset was inappropriate.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto composite_type =
|
||||
context->get_type_mgr()->GetType(message_.composite_type_id());
|
||||
|
||||
if (!fuzzerutil::IsCompositeType(composite_type)) {
|
||||
// The type must actually be a composite.
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the type is an array, matrix, struct or vector, the components need to
|
||||
// be suitable for constructing something of that type.
|
||||
if (composite_type->AsArray() && !ComponentsForArrayConstructionAreOK(
|
||||
context, *composite_type->AsArray())) {
|
||||
return false;
|
||||
}
|
||||
if (composite_type->AsMatrix() && !ComponentsForMatrixConstructionAreOK(
|
||||
context, *composite_type->AsMatrix())) {
|
||||
return false;
|
||||
}
|
||||
if (composite_type->AsStruct() && !ComponentsForStructConstructionAreOK(
|
||||
context, *composite_type->AsStruct())) {
|
||||
return false;
|
||||
}
|
||||
if (composite_type->AsVector() && !ComponentsForVectorConstructionAreOK(
|
||||
context, *composite_type->AsVector())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now check whether every component being used to initialize the composite is
|
||||
// available at the desired program point.
|
||||
for (auto& component : message_.component()) {
|
||||
auto component_inst = context->get_def_use_mgr()->GetDef(component);
|
||||
if (!context->get_instr_block(component)) {
|
||||
// The component does not have a block; that means it is in global scope,
|
||||
// which is OK. (Whether the component actually corresponds to an
|
||||
// instruction is checked above when determining whether types are
|
||||
// suitable.)
|
||||
continue;
|
||||
}
|
||||
// Check whether the component is available.
|
||||
if (insert_before->HasResultId() &&
|
||||
insert_before->result_id() == component) {
|
||||
// This constitutes trying to use an id right before it is defined. The
|
||||
// special case is needed due to an instruction always dominating itself.
|
||||
return false;
|
||||
}
|
||||
if (!context
|
||||
->GetDominatorAnalysis(
|
||||
context->get_instr_block(&*insert_before)->GetParent())
|
||||
->Dominates(component_inst, &*insert_before)) {
|
||||
// The instruction defining the component must dominate the instruction we
|
||||
// wish to insert the composite before.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationConstructComposite::Apply(opt::IRContext* context,
|
||||
FactManager* fact_manager) const {
|
||||
// Use the base and offset information from the transformation to determine
|
||||
// where in the module a new instruction should be inserted.
|
||||
auto base_instruction =
|
||||
context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
|
||||
auto destination_block = context->get_instr_block(base_instruction);
|
||||
auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
|
||||
destination_block, base_instruction, message_.offset());
|
||||
|
||||
// Prepare the input operands for an OpCompositeConstruct instruction.
|
||||
opt::Instruction::OperandList in_operands;
|
||||
for (auto& component_id : message_.component()) {
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {component_id}});
|
||||
}
|
||||
|
||||
// Insert an OpCompositeConstruct instruction.
|
||||
insert_before.InsertBefore(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpCompositeConstruct, message_.composite_type_id(),
|
||||
message_.fresh_id(), in_operands));
|
||||
|
||||
// Inform the fact manager that we now have new synonyms: every component of
|
||||
// the composite is synonymous with the id used to construct that component.
|
||||
auto composite_type =
|
||||
context->get_type_mgr()->GetType(message_.composite_type_id());
|
||||
uint32_t index = 0;
|
||||
for (auto component : message_.component()) {
|
||||
protobufs::Fact fact;
|
||||
fact.mutable_id_synonym_fact()->set_id(component);
|
||||
fact.mutable_id_synonym_fact()->mutable_data_descriptor()->set_object(
|
||||
message_.fresh_id());
|
||||
fact.mutable_id_synonym_fact()->mutable_data_descriptor()->add_index(index);
|
||||
fact_manager->AddFact(fact, context);
|
||||
if (composite_type->AsVector()) {
|
||||
// The vector case is a bit fiddly, because one argument to a vector
|
||||
// constructor can cover more than one element.
|
||||
auto component_type = context->get_type_mgr()->GetType(
|
||||
context->get_def_use_mgr()->GetDef(component)->type_id());
|
||||
if (component_type->AsVector()) {
|
||||
assert(component_type->AsVector()->element_type() ==
|
||||
composite_type->AsVector()->element_type());
|
||||
index += component_type->AsVector()->element_count();
|
||||
} else {
|
||||
assert(component_type == composite_type->AsVector()->element_type());
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
// The non-vector cases are all easy: the constructor has exactly the same
|
||||
// number of arguments as the number of sub-components, so we can just
|
||||
// increment the index.
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
bool TransformationConstructComposite::ComponentsForArrayConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Array& array_type) const {
|
||||
if (array_type.length_info().words[0] !=
|
||||
opt::analysis::Array::LengthInfo::kConstant) {
|
||||
// We only handle constant-sized arrays.
|
||||
return false;
|
||||
}
|
||||
if (array_type.length_info().words.size() != 2) {
|
||||
// We only handle the case where the array size can be captured in a single
|
||||
// word.
|
||||
return false;
|
||||
}
|
||||
// Get the array size.
|
||||
auto array_size = array_type.length_info().words[1];
|
||||
if (static_cast<uint32_t>(message_.component().size()) != array_size) {
|
||||
// The number of components must match the array size.
|
||||
return false;
|
||||
}
|
||||
// Check that each component is the result id of an instruction whose type is
|
||||
// the array's element type.
|
||||
for (auto component_id : message_.component()) {
|
||||
auto inst = context->get_def_use_mgr()->GetDef(component_id);
|
||||
if (inst == nullptr || !inst->type_id()) {
|
||||
// The component does not correspond to an instruction with a result
|
||||
// type.
|
||||
return false;
|
||||
}
|
||||
auto component_type = context->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(component_type);
|
||||
if (component_type != array_type.element_type()) {
|
||||
// The component's type does not match the array's element type.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransformationConstructComposite::ComponentsForMatrixConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const {
|
||||
if (static_cast<uint32_t>(message_.component().size()) !=
|
||||
matrix_type.element_count()) {
|
||||
// The number of components must match the number of columns of the matrix.
|
||||
return false;
|
||||
}
|
||||
// Check that each component is the result id of an instruction whose type is
|
||||
// the matrix's column type.
|
||||
for (auto component_id : message_.component()) {
|
||||
auto inst = context->get_def_use_mgr()->GetDef(component_id);
|
||||
if (inst == nullptr || !inst->type_id()) {
|
||||
// The component does not correspond to an instruction with a result
|
||||
// type.
|
||||
return false;
|
||||
}
|
||||
auto component_type = context->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(component_type);
|
||||
if (component_type != matrix_type.element_type()) {
|
||||
// The component's type does not match the matrix's column type.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransformationConstructComposite::ComponentsForStructConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Struct& struct_type) const {
|
||||
if (static_cast<uint32_t>(message_.component().size()) !=
|
||||
struct_type.element_types().size()) {
|
||||
// The number of components must match the number of fields of the struct.
|
||||
return false;
|
||||
}
|
||||
// Check that each component is the result id of an instruction those type
|
||||
// matches the associated field type.
|
||||
for (uint32_t field_index = 0;
|
||||
field_index < struct_type.element_types().size(); field_index++) {
|
||||
auto inst =
|
||||
context->get_def_use_mgr()->GetDef(message_.component()[field_index]);
|
||||
if (inst == nullptr || !inst->type_id()) {
|
||||
// The component does not correspond to an instruction with a result
|
||||
// type.
|
||||
return false;
|
||||
}
|
||||
auto component_type = context->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(component_type);
|
||||
if (component_type != struct_type.element_types()[field_index]) {
|
||||
// The component's type does not match the corresponding field type.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransformationConstructComposite::ComponentsForVectorConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Vector& vector_type) const {
|
||||
uint32_t base_element_count = 0;
|
||||
auto element_type = vector_type.element_type();
|
||||
for (auto& component_id : message_.component()) {
|
||||
auto inst = context->get_def_use_mgr()->GetDef(component_id);
|
||||
if (inst == nullptr || !inst->type_id()) {
|
||||
// The component does not correspond to an instruction with a result
|
||||
// type.
|
||||
return false;
|
||||
}
|
||||
auto component_type = context->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(component_type);
|
||||
if (component_type == element_type) {
|
||||
base_element_count++;
|
||||
} else if (component_type->AsVector() &&
|
||||
component_type->AsVector()->element_type() == element_type) {
|
||||
base_element_count += component_type->AsVector()->element_count();
|
||||
} else {
|
||||
// The component was not appropriate; e.g. no type corresponding to the
|
||||
// given id was found, or the type that was found was not compatible
|
||||
// with the vector being constructed.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// The number of components provided (when vector components are flattened
|
||||
// out) needs to match the length of the vector being constructed.
|
||||
return base_element_count == vector_type.element_count();
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationConstructComposite::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_construct_composite() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
88
3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h
vendored
Normal file
88
3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// 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_CONSTRUCT_COMPOSITE_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_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 TransformationConstructComposite : public Transformation {
|
||||
public:
|
||||
explicit TransformationConstructComposite(
|
||||
const protobufs::TransformationConstructComposite& message);
|
||||
|
||||
TransformationConstructComposite(uint32_t composite_type_id,
|
||||
std::vector<uint32_t> component,
|
||||
uint32_t base_instruction_id,
|
||||
uint32_t offset, uint32_t fresh_id);
|
||||
|
||||
// - |message_.fresh_id| must not be used by the module.
|
||||
// - |message_.composite_type_id| must be the id of a composite type
|
||||
// - The elements of |message_.component| must be result ids that are
|
||||
// suitable for constructing an element of the given composite type, in
|
||||
// order
|
||||
// - The elements of |message_.component| must not be the target of any
|
||||
// decorations.
|
||||
// - |message_.base_instruction_id| must be the result id of an instruction
|
||||
// 'base' in some block 'blk'.
|
||||
// - 'blk' must contain an instruction 'inst' located |message_.offset|
|
||||
// instructions after 'base' (if |message_.offset| = 0 then 'inst' =
|
||||
// 'base').
|
||||
// - It must be legal to insert an OpCompositeConstruct instruction directly
|
||||
// before 'inst'.
|
||||
// - Each element of |message_.component| must be available directly before
|
||||
// 'inst'.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
|
||||
// Inserts a new OpCompositeConstruct instruction, with id
|
||||
// |message_.fresh_id|, directly before the instruction identified by
|
||||
// |message_.base_instruction_id| and |message_.offset|. The instruction
|
||||
// creates a composite of type |message_.composite_type_id| using the ids of
|
||||
// |message_.component|.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
// Helper to decide whether the components of the transformation are suitable
|
||||
// for constructing an array of the given type.
|
||||
bool ComponentsForArrayConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Array& array_type) const;
|
||||
|
||||
// Similar, but for matrices.
|
||||
bool ComponentsForMatrixConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const;
|
||||
|
||||
// Similar, but for structs.
|
||||
bool ComponentsForStructConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Struct& struct_type) const;
|
||||
|
||||
// Similar, but for vectors.
|
||||
bool ComponentsForVectorConstructionAreOK(
|
||||
opt::IRContext* context, const opt::analysis::Vector& vector_type) const;
|
||||
|
||||
protobufs::TransformationConstructComposite message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_
|
||||
@@ -46,7 +46,7 @@ bool TransformationCopyObject::IsApplicable(
|
||||
if (!object_inst) {
|
||||
return false;
|
||||
}
|
||||
if (!IsCopyable(context, object_inst)) {
|
||||
if (!fuzzerutil::CanMakeSynonymOf(context, object_inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ bool TransformationCopyObject::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanInsertCopyBefore(insert_before)) {
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
|
||||
insert_before)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -91,13 +92,6 @@ bool TransformationCopyObject::IsApplicable(
|
||||
|
||||
void TransformationCopyObject::Apply(opt::IRContext* context,
|
||||
FactManager* fact_manager) const {
|
||||
// - A new instruction,
|
||||
// %|message_.fresh_id| = OpCopyObject %ty %|message_.object|
|
||||
// is added directly before the instruction at |message_.insert_after_id| +
|
||||
// |message_|.offset, where %ty is the type of |message_.object|.
|
||||
// - The fact that |message_.fresh_id| and |message_.object| are synonyms
|
||||
// is added to the fact manager.
|
||||
// The id of the object to be copied must exist
|
||||
auto object_inst = context->get_def_use_mgr()->GetDef(message_.object());
|
||||
assert(object_inst && "The object to be copied must exist.");
|
||||
auto base_instruction =
|
||||
@@ -132,43 +126,5 @@ protobufs::Transformation TransformationCopyObject::ToMessage() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationCopyObject::IsCopyable(opt::IRContext* ir_context,
|
||||
opt::Instruction* inst) {
|
||||
if (!inst->HasResultId()) {
|
||||
// We can only apply OpCopyObject to instructions that generate ids.
|
||||
return false;
|
||||
}
|
||||
if (!inst->type_id()) {
|
||||
// We can only apply OpCopyObject to instructions that have types.
|
||||
return false;
|
||||
}
|
||||
// We do not copy objects that have decorations: if the copy is not
|
||||
// decorated analogously, using the original object vs. its copy may not be
|
||||
// equivalent.
|
||||
// TODO(afd): it would be possible to make the copy but not add an id
|
||||
// synonym.
|
||||
return ir_context->get_decoration_mgr()
|
||||
->GetDecorationsFor(inst->result_id(), true)
|
||||
.empty();
|
||||
}
|
||||
|
||||
bool TransformationCopyObject::CanInsertCopyBefore(
|
||||
const opt::BasicBlock::iterator& instruction_in_block) {
|
||||
if (instruction_in_block->PreviousNode() &&
|
||||
(instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
|
||||
instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
|
||||
// We cannot insert a copy directly after a merge instruction.
|
||||
return false;
|
||||
}
|
||||
if (instruction_in_block->opcode() == SpvOpVariable) {
|
||||
// We cannot insert a copy directly before a variable; variables in a
|
||||
// function must be contiguous in the entry block.
|
||||
return false;
|
||||
}
|
||||
// We cannot insert a copy directly before OpPhi, because OpPhi instructions
|
||||
// need to be contiguous at the start of a block.
|
||||
return instruction_in_block->opcode() != SpvOpPhi;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -37,14 +37,14 @@ class TransformationCopyObject : public Transformation {
|
||||
// has a result type
|
||||
// - |message_.object| must not be the target of any decoration.
|
||||
// TODO(afd): consider copying decorations along with objects.
|
||||
// - |message_.insert_after_id| must be the result id of an instruction
|
||||
// - |message_.base_instruction_id| must be the result id of an instruction
|
||||
// 'base' in some block 'blk'.
|
||||
// - 'blk' must contain an instruction 'inst' located |message_.offset|
|
||||
// instructions after 'base' (if |message_.offset| = 0 then 'inst' =
|
||||
// 'base').
|
||||
// - It must be legal to insert an OpCopyObject instruction directly
|
||||
// before 'inst'.
|
||||
// - |message_object| must be available directly before 'inst'.
|
||||
// - |message_.object| must be available directly before 'inst'.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
|
||||
@@ -58,14 +58,6 @@ class TransformationCopyObject : public Transformation {
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Determines whether it is OK to make a copy of |inst|.
|
||||
static bool IsCopyable(opt::IRContext* ir_context, opt::Instruction* inst);
|
||||
|
||||
// Determines whether it is OK to insert a copy instruction before the given
|
||||
// instruction.
|
||||
static bool CanInsertCopyBefore(
|
||||
const opt::BasicBlock::iterator& instruction_in_block);
|
||||
|
||||
private:
|
||||
protobufs::TransformationCopyObject message_;
|
||||
};
|
||||
|
||||
@@ -238,7 +238,7 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
|
||||
|
||||
// The id use descriptor must identify some instruction
|
||||
auto instruction =
|
||||
transformation::FindInstruction(message_.id_use_descriptor(), context);
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), context);
|
||||
if (instruction == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -268,7 +268,7 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
|
||||
message_.fresh_id_for_binary_operation(), operands);
|
||||
opt::Instruction* result = binary_instruction.get();
|
||||
auto instruction_containing_constant_use =
|
||||
transformation::FindInstruction(message_.id_use_descriptor(), context);
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), context);
|
||||
|
||||
// We want to insert the new instruction before the instruction that contains
|
||||
// the use of the boolean, but we need to go backwards one more instruction if
|
||||
|
||||
@@ -149,7 +149,7 @@ bool TransformationReplaceConstantWithUniform::IsApplicable(
|
||||
// The id use descriptor must identify some instruction with respect to the
|
||||
// module.
|
||||
auto instruction_using_constant =
|
||||
transformation::FindInstruction(message_.id_use_descriptor(), context);
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), context);
|
||||
if (!instruction_using_constant) {
|
||||
return false;
|
||||
}
|
||||
@@ -188,7 +188,7 @@ void TransformationReplaceConstantWithUniform::Apply(
|
||||
spvtools::fuzz::FactManager* /*unused*/) const {
|
||||
// Get the instruction that contains the id use we wish to replace.
|
||||
auto instruction_containing_constant_use =
|
||||
transformation::FindInstruction(message_.id_use_descriptor(), context);
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), context);
|
||||
assert(instruction_containing_constant_use &&
|
||||
"Precondition requires that the id use can be found.");
|
||||
assert(instruction_containing_constant_use->GetSingleWordInOperand(
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/opt/types.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@@ -33,9 +34,7 @@ TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
|
||||
protobufs::IdUseDescriptor id_use_descriptor,
|
||||
protobufs::DataDescriptor data_descriptor,
|
||||
uint32_t fresh_id_for_temporary) {
|
||||
assert(fresh_id_for_temporary == 0 && data_descriptor.index().size() == 0 &&
|
||||
"At present we do not support making an id that is synonymous with an "
|
||||
"index into a composite.");
|
||||
assert((fresh_id_for_temporary == 0) == (data_descriptor.index().empty()));
|
||||
*message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
|
||||
*message_.mutable_data_descriptor() = std::move(data_descriptor);
|
||||
message_.set_fresh_id_for_temporary(fresh_id_for_temporary);
|
||||
@@ -61,12 +60,15 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Does the id use descriptor in the transformation identify an instruction?
|
||||
auto use_instruction =
|
||||
transformation::FindInstruction(message_.id_use_descriptor(), context);
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), context);
|
||||
if (!use_instruction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is it legitimate to replace the use identified by the id use descriptor
|
||||
// with a synonym?
|
||||
if (!ReplacingUseWithSynonymIsOk(
|
||||
context, use_instruction,
|
||||
message_.id_use_descriptor().in_operand_index(),
|
||||
@@ -74,8 +76,23 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(message_.fresh_id_for_temporary() == 0);
|
||||
assert(message_.data_descriptor().index().empty());
|
||||
if (message_.fresh_id_for_temporary() == 0) {
|
||||
if (!message_.data_descriptor().index().empty()) {
|
||||
// If we have no id to use as a temporary variable, we should not have any
|
||||
// indices to extract from.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_temporary())) {
|
||||
// The id to be used as a temporary needs to be fresh.
|
||||
return false;
|
||||
}
|
||||
if (message_.data_descriptor().index_size() != 1) {
|
||||
// At present we support just a single index to allow extracting directly
|
||||
// from a composite.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -83,12 +100,79 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
|
||||
void TransformationReplaceIdWithSynonym::Apply(
|
||||
spvtools::opt::IRContext* context,
|
||||
spvtools::fuzz::FactManager* /*unused*/) const {
|
||||
assert(message_.data_descriptor().index().empty());
|
||||
auto instruction_to_change =
|
||||
transformation::FindInstruction(message_.id_use_descriptor(), context);
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), context);
|
||||
|
||||
// Ultimately we are going to replace the id use identified in the
|
||||
// transformation with |replacement_id|, which will either be the synonym's
|
||||
// id, or the id of a temporary used to extract the synonym from a composite.
|
||||
uint32_t replacement_id;
|
||||
|
||||
if (message_.fresh_id_for_temporary()) {
|
||||
// The transformation having a temporary variable means that we need to
|
||||
// extract the synonym from a composite.
|
||||
|
||||
uint32_t type_id_of_id_to_be_replaced =
|
||||
context->get_def_use_mgr()
|
||||
->GetDef(message_.id_use_descriptor().id_of_interest())
|
||||
->type_id();
|
||||
opt::analysis::Type* type_of_id_to_be_replaced =
|
||||
context->get_type_mgr()->GetType(type_id_of_id_to_be_replaced);
|
||||
opt::analysis::Type* type_of_composite = context->get_type_mgr()->GetType(
|
||||
context->get_def_use_mgr()
|
||||
->GetDef(message_.data_descriptor().object())
|
||||
->type_id());
|
||||
|
||||
// Intuitively we want to make an OpCompositeExtract instruction, to get the
|
||||
// synonym out of the composite. But in the case of a vector, the synonym
|
||||
// might involve multiple vector indices; e.g. the y and z components of a
|
||||
// vec4 might be synonymous with a vec2, and in that case OpCompositeExtract
|
||||
// doesn't give us what we want; we need to use OpVectorShuffle instead.
|
||||
std::unique_ptr<opt::Instruction> new_instruction;
|
||||
if (type_of_composite->AsVector() &&
|
||||
type_of_composite->AsVector()->element_type() !=
|
||||
type_of_id_to_be_replaced) {
|
||||
// We need to extract a vector from inside a vector, so we will need to
|
||||
// use OpVectorShuffle.
|
||||
|
||||
assert(type_of_id_to_be_replaced->AsVector());
|
||||
assert(type_of_id_to_be_replaced->AsVector()->element_type() ==
|
||||
type_of_composite->AsVector()->element_type());
|
||||
opt::Instruction::OperandList shuffle_operands = {
|
||||
{SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}},
|
||||
{SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}}};
|
||||
for (uint32_t i = 0;
|
||||
i < type_of_id_to_be_replaced->AsVector()->element_count(); i++) {
|
||||
shuffle_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{message_.data_descriptor().index(0) + i}});
|
||||
}
|
||||
new_instruction = MakeUnique<opt::Instruction>(
|
||||
context, SpvOpVectorShuffle, type_id_of_id_to_be_replaced,
|
||||
message_.fresh_id_for_temporary(), shuffle_operands);
|
||||
} else {
|
||||
// We are either extracting from a non-vector, or extracting a scalar from
|
||||
// a vector, so we can use OpCompositeExtract.
|
||||
opt::Instruction::OperandList extract_operands = {
|
||||
{SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{message_.data_descriptor().index(0)}}};
|
||||
new_instruction = MakeUnique<opt::Instruction>(
|
||||
context, SpvOpCompositeExtract, type_id_of_id_to_be_replaced,
|
||||
message_.fresh_id_for_temporary(), extract_operands);
|
||||
}
|
||||
instruction_to_change->InsertBefore(std::move(new_instruction));
|
||||
|
||||
// The replacement id is the temporary variable we used to extract the
|
||||
// synonym from a composite.
|
||||
replacement_id = message_.fresh_id_for_temporary();
|
||||
fuzzerutil::UpdateModuleIdBound(context, replacement_id);
|
||||
} else {
|
||||
// The replacement id is the synonym's id.
|
||||
replacement_id = message_.data_descriptor().object();
|
||||
}
|
||||
|
||||
instruction_to_change->SetInOperand(
|
||||
message_.id_use_descriptor().in_operand_index(),
|
||||
{message_.data_descriptor().object()});
|
||||
message_.id_use_descriptor().in_operand_index(), {replacement_id});
|
||||
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
|
||||
100
3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.cpp
vendored
Normal file
100
3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.cpp
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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_function_control.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationSetFunctionControl::TransformationSetFunctionControl(
|
||||
const spvtools::fuzz::protobufs::TransformationSetFunctionControl& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationSetFunctionControl::TransformationSetFunctionControl(
|
||||
uint32_t function_id, uint32_t function_control) {
|
||||
message_.set_function_id(function_id);
|
||||
message_.set_function_control(function_control);
|
||||
}
|
||||
|
||||
bool TransformationSetFunctionControl::IsApplicable(
|
||||
opt::IRContext* context, const FactManager& /*unused*/) const {
|
||||
opt::Instruction* function_def_instruction =
|
||||
FindFunctionDefInstruction(context);
|
||||
if (!function_def_instruction) {
|
||||
// The given function id does not correspond to any function.
|
||||
return false;
|
||||
}
|
||||
uint32_t existing_function_control_mask =
|
||||
function_def_instruction->GetSingleWordInOperand(0);
|
||||
|
||||
// Check (via an assertion) that function control mask doesn't have any bad
|
||||
// bits set.
|
||||
uint32_t acceptable_function_control_bits =
|
||||
SpvFunctionControlInlineMask | SpvFunctionControlDontInlineMask |
|
||||
SpvFunctionControlPureMask | SpvFunctionControlConstMask;
|
||||
// The following is to keep release-mode compilers happy as this variable is
|
||||
// only used in an assertion.
|
||||
(void)(acceptable_function_control_bits);
|
||||
assert(!(message_.function_control() & ~acceptable_function_control_bits) &&
|
||||
"Nonsensical loop control bits were found.");
|
||||
|
||||
// Check (via an assertion) that function control mask does not have both
|
||||
// Inline and DontInline bits set.
|
||||
assert(!((message_.function_control() & SpvFunctionControlInlineMask) &&
|
||||
(message_.function_control() & SpvFunctionControlDontInlineMask)) &&
|
||||
"It is not OK to set both the 'Inline' and 'DontInline' bits of a "
|
||||
"function control mask");
|
||||
|
||||
// Check that Const and Pure are only present if they were present on the
|
||||
// original function
|
||||
for (auto mask_bit :
|
||||
{SpvFunctionControlPureMask, SpvFunctionControlConstMask}) {
|
||||
if ((message_.function_control() & mask_bit) &&
|
||||
!(existing_function_control_mask & mask_bit)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationSetFunctionControl::Apply(opt::IRContext* context,
|
||||
FactManager* /*unused*/) const {
|
||||
opt::Instruction* function_def_instruction =
|
||||
FindFunctionDefInstruction(context);
|
||||
function_def_instruction->SetInOperand(0, {message_.function_control()});
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationSetFunctionControl::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_set_function_control() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction(
|
||||
opt::IRContext* context) const {
|
||||
// Look through all functions for a function whose defining instruction's
|
||||
// result id matches |message_.function_id|, returning the defining
|
||||
// instruction if found.
|
||||
for (auto& function : *context->module()) {
|
||||
if (function.DefInst().result_id() == message_.function_id()) {
|
||||
return &function.DefInst();
|
||||
}
|
||||
}
|
||||
// A nullptr result indicates that no match was found.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
58
3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.h
vendored
Normal file
58
3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.h
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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_FUNCTION_CONTROL_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_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 TransformationSetFunctionControl : public Transformation {
|
||||
public:
|
||||
explicit TransformationSetFunctionControl(
|
||||
const protobufs::TransformationSetFunctionControl& message);
|
||||
|
||||
TransformationSetFunctionControl(uint32_t function_id,
|
||||
uint32_t function_control);
|
||||
|
||||
// - |message_.function_id| must be the result id of an OpFunction
|
||||
// instruction.
|
||||
// - |message_.function_control| must be a function control mask that sets
|
||||
// at most one of 'Inline' or 'DontInline', and that may not contain 'Pure'
|
||||
// (respectively 'Const') unless the existing function control mask contains
|
||||
// 'Pure' (respectively 'Const').
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
|
||||
// The function control operand of instruction |message_.function_id| is
|
||||
// over-written with |message_.function_control|.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
opt::Instruction* FindFunctionDefInstruction(opt::IRContext* context) const;
|
||||
|
||||
protobufs::TransformationSetFunctionControl message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
|
||||
216
3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp
vendored
Normal file
216
3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp
vendored
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
3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h
vendored
Normal file
79
3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h
vendored
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_
|
||||
60
3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.cpp
vendored
Normal file
60
3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.cpp
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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_selection_control.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationSetSelectionControl::TransformationSetSelectionControl(
|
||||
const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationSetSelectionControl::TransformationSetSelectionControl(
|
||||
uint32_t block_id, uint32_t selection_control) {
|
||||
message_.set_block_id(block_id);
|
||||
message_.set_selection_control(selection_control);
|
||||
}
|
||||
|
||||
bool TransformationSetSelectionControl::IsApplicable(
|
||||
opt::IRContext* context, const FactManager& /*unused*/) const {
|
||||
assert((message_.selection_control() == SpvSelectionControlMaskNone ||
|
||||
message_.selection_control() == SpvSelectionControlFlattenMask ||
|
||||
message_.selection_control() == SpvSelectionControlDontFlattenMask) &&
|
||||
"Selection control should never be set to something other than "
|
||||
"'None', 'Flatten' or 'DontFlatten'");
|
||||
if (auto block = context->get_instr_block(message_.block_id())) {
|
||||
if (auto merge_inst = block->GetMergeInst()) {
|
||||
return merge_inst->opcode() == SpvOpSelectionMerge;
|
||||
}
|
||||
}
|
||||
// Either the block did not exit, or did not end with OpSelectionMerge.
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransformationSetSelectionControl::Apply(opt::IRContext* context,
|
||||
FactManager* /*unused*/) const {
|
||||
context->get_instr_block(message_.block_id())
|
||||
->GetMergeInst()
|
||||
->SetInOperand(1, {message_.selection_control()});
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationSetSelectionControl::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_set_selection_control() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
54
3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h
vendored
Normal file
54
3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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_SELECTION_CONTROL_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_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 TransformationSetSelectionControl : public Transformation {
|
||||
public:
|
||||
explicit TransformationSetSelectionControl(
|
||||
const protobufs::TransformationSetSelectionControl& message);
|
||||
|
||||
TransformationSetSelectionControl(uint32_t block_id,
|
||||
uint32_t selection_control);
|
||||
|
||||
// - |message_.block_id| must be a block containing an OpSelectionMerge
|
||||
// instruction.
|
||||
// - |message_.selection_control| must be one of None, Flatten or
|
||||
// DontFlatten.
|
||||
bool IsApplicable(opt::IRContext* context,
|
||||
const FactManager& fact_manager) const override;
|
||||
|
||||
// - The selection control operand of the OpSelectionMergeInstruction in
|
||||
// |message_.block_id| is overwritten with |message_.selection_control|.
|
||||
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationSetSelectionControl message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
|
||||
@@ -94,6 +94,14 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
if (analyses_to_invalidate & kAnalysisTypes) {
|
||||
analyses_to_invalidate |= kAnalysisConstants;
|
||||
}
|
||||
|
||||
// The dominator analysis hold the psuedo entry and exit nodes from the CFG.
|
||||
// Also if the CFG change the dominators many changed as well, so the
|
||||
// dominator analysis should be invalidated as well.
|
||||
if (analyses_to_invalidate & kAnalysisCFG) {
|
||||
analyses_to_invalidate |= kAnalysisDominatorAnalysis;
|
||||
}
|
||||
|
||||
if (analyses_to_invalidate & kAnalysisDefUse) {
|
||||
def_use_mgr_.reset(nullptr);
|
||||
}
|
||||
|
||||
30
3rdparty/spirv-tools/source/opt/type_manager.cpp
vendored
30
3rdparty/spirv-tools/source/opt/type_manager.cpp
vendored
@@ -409,6 +409,22 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
|
||||
{static_cast<uint32_t>(
|
||||
type->AsForwardPointer()->storage_class())}}});
|
||||
break;
|
||||
case Type::kCooperativeMatrixNV: {
|
||||
auto coop_mat = type->AsCooperativeMatrixNV();
|
||||
uint32_t const component_type =
|
||||
GetTypeInstruction(coop_mat->component_type());
|
||||
if (component_type == 0) {
|
||||
return 0;
|
||||
}
|
||||
typeInst = MakeUnique<Instruction>(
|
||||
context(), SpvOpTypeCooperativeMatrixNV, 0, id,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {component_type}},
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "Unexpected type");
|
||||
break;
|
||||
@@ -604,6 +620,14 @@ Type* TypeManager::RebuildType(const Type& type) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::kCooperativeMatrixNV: {
|
||||
const CooperativeMatrixNV* cm_type = type.AsCooperativeMatrixNV();
|
||||
const Type* component_type = cm_type->component_type();
|
||||
rebuilt_ty = MakeUnique<CooperativeMatrixNV>(
|
||||
RebuildType(*component_type), cm_type->scope_id(), cm_type->rows_id(),
|
||||
cm_type->columns_id());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "Unhandled type");
|
||||
return nullptr;
|
||||
@@ -832,6 +856,12 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) {
|
||||
case SpvOpTypeAccelerationStructureNV:
|
||||
type = new AccelerationStructureNV();
|
||||
break;
|
||||
case SpvOpTypeCooperativeMatrixNV:
|
||||
type = new CooperativeMatrixNV(GetType(inst.GetSingleWordInOperand(0)),
|
||||
inst.GetSingleWordInOperand(1),
|
||||
inst.GetSingleWordInOperand(2),
|
||||
inst.GetSingleWordInOperand(3));
|
||||
break;
|
||||
default:
|
||||
SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
|
||||
break;
|
||||
|
||||
41
3rdparty/spirv-tools/source/opt/types.cpp
vendored
41
3rdparty/spirv-tools/source/opt/types.cpp
vendored
@@ -127,6 +127,7 @@ std::unique_ptr<Type> Type::Clone() const {
|
||||
DeclareKindCase(PipeStorage);
|
||||
DeclareKindCase(NamedBarrier);
|
||||
DeclareKindCase(AccelerationStructureNV);
|
||||
DeclareKindCase(CooperativeMatrixNV);
|
||||
#undef DeclareKindCase
|
||||
default:
|
||||
assert(false && "Unhandled type");
|
||||
@@ -171,6 +172,7 @@ bool Type::operator==(const Type& other) const {
|
||||
DeclareKindCase(PipeStorage);
|
||||
DeclareKindCase(NamedBarrier);
|
||||
DeclareKindCase(AccelerationStructureNV);
|
||||
DeclareKindCase(CooperativeMatrixNV);
|
||||
#undef DeclareKindCase
|
||||
default:
|
||||
assert(false && "Unhandled type");
|
||||
@@ -220,6 +222,7 @@ void Type::GetHashWords(std::vector<uint32_t>* words,
|
||||
DeclareKindCase(PipeStorage);
|
||||
DeclareKindCase(NamedBarrier);
|
||||
DeclareKindCase(AccelerationStructureNV);
|
||||
DeclareKindCase(CooperativeMatrixNV);
|
||||
#undef DeclareKindCase
|
||||
default:
|
||||
assert(false && "Unhandled type");
|
||||
@@ -654,6 +657,44 @@ void ForwardPointer::GetExtraHashWords(
|
||||
if (pointer_) pointer_->GetHashWords(words, seen);
|
||||
}
|
||||
|
||||
CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope,
|
||||
const uint32_t rows,
|
||||
const uint32_t columns)
|
||||
: Type(kCooperativeMatrixNV),
|
||||
component_type_(type),
|
||||
scope_id_(scope),
|
||||
rows_id_(rows),
|
||||
columns_id_(columns) {
|
||||
assert(type != nullptr);
|
||||
assert(scope != 0);
|
||||
assert(rows != 0);
|
||||
assert(columns != 0);
|
||||
}
|
||||
|
||||
std::string CooperativeMatrixNV::str() const {
|
||||
std::ostringstream oss;
|
||||
oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_
|
||||
<< ", " << columns_id_ << ">";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void CooperativeMatrixNV::GetExtraHashWords(
|
||||
std::vector<uint32_t>* words, std::unordered_set<const Type*>* pSet) const {
|
||||
component_type_->GetHashWords(words, pSet);
|
||||
words->push_back(scope_id_);
|
||||
words->push_back(rows_id_);
|
||||
words->push_back(columns_id_);
|
||||
}
|
||||
|
||||
bool CooperativeMatrixNV::IsSameImpl(const Type* that,
|
||||
IsSameCache* seen) const {
|
||||
const CooperativeMatrixNV* mt = that->AsCooperativeMatrixNV();
|
||||
if (!mt) return false;
|
||||
return component_type_->IsSameImpl(mt->component_type_, seen) &&
|
||||
scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ &&
|
||||
columns_id_ == mt->columns_id_ && HasSameDecorations(that);
|
||||
}
|
||||
|
||||
} // namespace analysis
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
33
3rdparty/spirv-tools/source/opt/types.h
vendored
33
3rdparty/spirv-tools/source/opt/types.h
vendored
@@ -58,6 +58,7 @@ class ForwardPointer;
|
||||
class PipeStorage;
|
||||
class NamedBarrier;
|
||||
class AccelerationStructureNV;
|
||||
class CooperativeMatrixNV;
|
||||
|
||||
// Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods,
|
||||
// which is used as a way to probe the actual <subclass>.
|
||||
@@ -93,6 +94,7 @@ class Type {
|
||||
kPipeStorage,
|
||||
kNamedBarrier,
|
||||
kAccelerationStructureNV,
|
||||
kCooperativeMatrixNV
|
||||
};
|
||||
|
||||
Type(Kind k) : kind_(k) {}
|
||||
@@ -196,6 +198,7 @@ class Type {
|
||||
DeclareCastMethod(PipeStorage)
|
||||
DeclareCastMethod(NamedBarrier)
|
||||
DeclareCastMethod(AccelerationStructureNV)
|
||||
DeclareCastMethod(CooperativeMatrixNV)
|
||||
#undef DeclareCastMethod
|
||||
|
||||
protected:
|
||||
@@ -597,6 +600,36 @@ class ForwardPointer : public Type {
|
||||
const Pointer* pointer_;
|
||||
};
|
||||
|
||||
class CooperativeMatrixNV : public Type {
|
||||
public:
|
||||
CooperativeMatrixNV(const Type* type, const uint32_t scope,
|
||||
const uint32_t rows, const uint32_t columns);
|
||||
CooperativeMatrixNV(const CooperativeMatrixNV&) = default;
|
||||
|
||||
std::string str() const override;
|
||||
|
||||
CooperativeMatrixNV* AsCooperativeMatrixNV() override { return this; }
|
||||
const CooperativeMatrixNV* AsCooperativeMatrixNV() const override {
|
||||
return this;
|
||||
}
|
||||
|
||||
void GetExtraHashWords(std::vector<uint32_t>*,
|
||||
std::unordered_set<const Type*>*) const override;
|
||||
|
||||
const Type* component_type() const { return component_type_; }
|
||||
uint32_t scope_id() const { return scope_id_; }
|
||||
uint32_t rows_id() const { return rows_id_; }
|
||||
uint32_t columns_id() const { return columns_id_; }
|
||||
|
||||
private:
|
||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||
|
||||
const Type* component_type_;
|
||||
const uint32_t scope_id_;
|
||||
const uint32_t rows_id_;
|
||||
const uint32_t columns_id_;
|
||||
};
|
||||
|
||||
#define DefineParameterlessType(type, name) \
|
||||
class type : public Type { \
|
||||
public: \
|
||||
|
||||
2
3rdparty/spirv-tools/source/print.cpp
vendored
2
3rdparty/spirv-tools/source/print.cpp
vendored
@@ -15,7 +15,7 @@
|
||||
#include "source/print.h"
|
||||
|
||||
#if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
|
||||
defined(SPIRV_FREEBSD)
|
||||
defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN)
|
||||
namespace spvtools {
|
||||
|
||||
clr::reset::operator const char*() { return "\x1b[0m"; }
|
||||
|
||||
@@ -29,8 +29,6 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_instruction_reduction_opportunity.h
|
||||
remove_function_reduction_opportunity.h
|
||||
remove_function_reduction_opportunity_finder.h
|
||||
remove_opname_instruction_reduction_opportunity_finder.h
|
||||
remove_relaxed_precision_decoration_opportunity_finder.h
|
||||
remove_selection_reduction_opportunity.h
|
||||
remove_selection_reduction_opportunity_finder.h
|
||||
remove_unreferenced_instruction_reduction_opportunity_finder.h
|
||||
@@ -57,11 +55,9 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_function_reduction_opportunity.cpp
|
||||
remove_function_reduction_opportunity_finder.cpp
|
||||
remove_instruction_reduction_opportunity.cpp
|
||||
remove_relaxed_precision_decoration_opportunity_finder.cpp
|
||||
remove_selection_reduction_opportunity.cpp
|
||||
remove_selection_reduction_opportunity_finder.cpp
|
||||
remove_unreferenced_instruction_reduction_opportunity_finder.cpp
|
||||
remove_opname_instruction_reduction_opportunity_finder.cpp
|
||||
structured_loop_to_selection_reduction_opportunity.cpp
|
||||
structured_loop_to_selection_reduction_opportunity_finder.cpp
|
||||
conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
|
||||
|
||||
@@ -73,7 +73,7 @@ ConditionalBranchToSimpleConditionalBranchOpportunityFinder::
|
||||
result.push_back(
|
||||
MakeUnique<
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity>(
|
||||
block.terminator(), redirect_to_true));
|
||||
context, block.terminator(), redirect_to_true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,15 @@
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
|
||||
Instruction* conditional_branch_instruction, bool redirect_to_true)
|
||||
: conditional_branch_instruction_(conditional_branch_instruction),
|
||||
IRContext* context, Instruction* conditional_branch_instruction,
|
||||
bool redirect_to_true)
|
||||
: context_(context),
|
||||
conditional_branch_instruction_(conditional_branch_instruction),
|
||||
redirect_to_true_(redirect_to_true) {}
|
||||
|
||||
bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
|
||||
@@ -43,11 +46,24 @@ void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() {
|
||||
uint32_t operand_to_copy =
|
||||
redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex;
|
||||
|
||||
auto old_successor_block_id =
|
||||
conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
operand_to_modify);
|
||||
|
||||
// Do the branch redirection.
|
||||
conditional_branch_instruction_->SetInOperand(
|
||||
operand_to_modify,
|
||||
{conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
operand_to_copy)});
|
||||
|
||||
// The old successor block may have phi instructions; these will need to
|
||||
// respect the change in edges.
|
||||
AdaptPhiInstructionsForRemovedEdge(
|
||||
context_->get_instr_block(conditional_branch_instruction_)->id(),
|
||||
context_->cfg()->block(old_successor_block_id));
|
||||
|
||||
// We have changed the CFG.
|
||||
context_->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
|
||||
@@ -31,7 +31,8 @@ class ConditionalBranchToSimpleConditionalBranchReductionOpportunity
|
||||
// to the true target; otherwise, the true target will be changed to also
|
||||
// point to the false target.
|
||||
explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
|
||||
opt::Instruction* conditional_branch_instruction, bool redirect_to_true);
|
||||
opt::IRContext* context, opt::Instruction* conditional_branch_instruction,
|
||||
bool redirect_to_true);
|
||||
|
||||
bool PreconditionHolds() override;
|
||||
|
||||
@@ -39,6 +40,7 @@ class ConditionalBranchToSimpleConditionalBranchReductionOpportunity
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
opt::IRContext* context_;
|
||||
opt::Instruction* conditional_branch_instruction_;
|
||||
|
||||
// If true, the false target will be changed to point to the true target;
|
||||
|
||||
244
3rdparty/spirv-tools/source/reduce/reducer.cpp
vendored
244
3rdparty/spirv-tools/source/reduce/reducer.cpp
vendored
@@ -24,8 +24,6 @@
|
||||
#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_block_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_function_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
|
||||
#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
@@ -35,41 +33,32 @@
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
struct Reducer::Impl {
|
||||
explicit Impl(spv_target_env env) : target_env(env) {}
|
||||
|
||||
bool ReachedStepLimit(uint32_t current_step,
|
||||
spv_const_reducer_options options);
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
InterestingnessFunction interestingness_function;
|
||||
std::vector<std::unique_ptr<ReductionPass>> passes;
|
||||
};
|
||||
|
||||
Reducer::Reducer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
|
||||
Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {}
|
||||
|
||||
Reducer::~Reducer() = default;
|
||||
|
||||
void Reducer::SetMessageConsumer(MessageConsumer c) {
|
||||
for (auto& pass : impl_->passes) {
|
||||
for (auto& pass : passes_) {
|
||||
pass->SetMessageConsumer(c);
|
||||
}
|
||||
impl_->consumer = std::move(c);
|
||||
for (auto& pass : cleanup_passes_) {
|
||||
pass->SetMessageConsumer(c);
|
||||
}
|
||||
consumer_ = std::move(c);
|
||||
}
|
||||
|
||||
void Reducer::SetInterestingnessFunction(
|
||||
Reducer::InterestingnessFunction interestingness_function) {
|
||||
impl_->interestingness_function = std::move(interestingness_function);
|
||||
interestingness_function_ = std::move(interestingness_function);
|
||||
}
|
||||
|
||||
Reducer::ReductionResultStatus Reducer::Run(
|
||||
std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
|
||||
spv_const_reducer_options options,
|
||||
spv_validator_options validator_options) const {
|
||||
spv_validator_options validator_options) {
|
||||
std::vector<uint32_t> current_binary(std::move(binary_in));
|
||||
|
||||
spvtools::SpirvTools tools(impl_->target_env);
|
||||
spvtools::SpirvTools tools(target_env_);
|
||||
assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
|
||||
|
||||
// Keeps track of how many reduction attempts have been tried. Reduction
|
||||
@@ -79,113 +68,49 @@ Reducer::ReductionResultStatus Reducer::Run(
|
||||
// Initial state should be valid.
|
||||
if (!tools.Validate(¤t_binary[0], current_binary.size(),
|
||||
validator_options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return Reducer::ReductionResultStatus::kInitialStateInvalid;
|
||||
}
|
||||
|
||||
// Initial state should be interesting.
|
||||
if (!impl_->interestingness_function(current_binary, reductions_applied)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial state was not interesting; stopping.");
|
||||
if (!interestingness_function_(current_binary, reductions_applied)) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial state was not interesting; stopping.");
|
||||
return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
|
||||
}
|
||||
|
||||
// Determines whether, on completing one round of reduction passes, it is
|
||||
// worthwhile trying a further round.
|
||||
bool another_round_worthwhile = true;
|
||||
Reducer::ReductionResultStatus result =
|
||||
RunPasses(&passes_, options, validator_options, tools, ¤t_binary,
|
||||
&reductions_applied);
|
||||
|
||||
// Apply round after round of reduction passes until we hit the reduction
|
||||
// step limit, or deem that another round is not going to be worthwhile.
|
||||
while (!impl_->ReachedStepLimit(reductions_applied, options) &&
|
||||
another_round_worthwhile) {
|
||||
// At the start of a round of reduction passes, assume another round will
|
||||
// not be worthwhile unless we find evidence to the contrary.
|
||||
another_round_worthwhile = false;
|
||||
|
||||
// Iterate through the available passes
|
||||
for (auto& pass : impl_->passes) {
|
||||
// If this pass hasn't reached its minimum granularity then it's
|
||||
// worth eventually doing another round of reductions, in order to
|
||||
// try this pass at a finer granularity.
|
||||
another_round_worthwhile |= !pass->ReachedMinimumGranularity();
|
||||
|
||||
// Keep applying this pass at its current granularity until it stops
|
||||
// working or we hit the reduction step limit.
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
("Trying pass " + pass->GetName() + ".").c_str());
|
||||
do {
|
||||
auto maybe_result = pass->TryApplyReduction(current_binary);
|
||||
if (maybe_result.empty()) {
|
||||
// For this round, the pass has no more opportunities (chunks) to
|
||||
// apply, so move on to the next pass.
|
||||
impl_->consumer(
|
||||
SPV_MSG_INFO, nullptr, {},
|
||||
("Pass " + pass->GetName() + " did not make a reduction step.")
|
||||
.c_str());
|
||||
break;
|
||||
}
|
||||
bool interesting = false;
|
||||
std::stringstream stringstream;
|
||||
reductions_applied++;
|
||||
stringstream << "Pass " << pass->GetName() << " made reduction step "
|
||||
<< reductions_applied << ".";
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
(stringstream.str().c_str()));
|
||||
if (!tools.Validate(&maybe_result[0], maybe_result.size(),
|
||||
validator_options)) {
|
||||
// The reduction step went wrong and an invalid binary was produced.
|
||||
// By design, this shouldn't happen; this is a safeguard to stop an
|
||||
// invalid binary from being regarded as interesting.
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Reduction step produced an invalid binary.");
|
||||
if (options->fail_on_validation_error) {
|
||||
return Reducer::ReductionResultStatus::kStateInvalid;
|
||||
}
|
||||
} else if (impl_->interestingness_function(maybe_result,
|
||||
reductions_applied)) {
|
||||
// Success! The binary produced by this reduction step is
|
||||
// interesting, so make it the binary of interest henceforth, and
|
||||
// note that it's worth doing another round of reduction passes.
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Reduction step succeeded.");
|
||||
current_binary = std::move(maybe_result);
|
||||
interesting = true;
|
||||
another_round_worthwhile = true;
|
||||
}
|
||||
// We must call this before the next call to TryApplyReduction.
|
||||
pass->NotifyInteresting(interesting);
|
||||
// Bail out if the reduction step limit has been reached.
|
||||
} while (!impl_->ReachedStepLimit(reductions_applied, options));
|
||||
}
|
||||
if (result == Reducer::ReductionResultStatus::kComplete) {
|
||||
// Cleanup passes.
|
||||
result = RunPasses(&cleanup_passes_, options, validator_options, tools,
|
||||
¤t_binary, &reductions_applied);
|
||||
}
|
||||
|
||||
if (result == Reducer::ReductionResultStatus::kComplete) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
|
||||
}
|
||||
|
||||
// Even if the reduction has failed by this point (e.g. due to producing an
|
||||
// invalid binary), we still update the output binary for better debugging.
|
||||
*binary_out = std::move(current_binary);
|
||||
|
||||
// Report whether reduction completed, or bailed out early due to reaching
|
||||
// the step limit.
|
||||
if (impl_->ReachedStepLimit(reductions_applied, options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Reached reduction step limit; stopping.");
|
||||
return Reducer::ReductionResultStatus::kReachedStepLimit;
|
||||
}
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
|
||||
return Reducer::ReductionResultStatus::kComplete;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Reducer::AddDefaultReductionPasses() {
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
RemoveOpNameInstructionReductionOpportunityFinder>());
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
RemoveRelaxedPrecisionDecorationOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder>(false));
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder>());
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
StructuredLoopToSelectionReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
@@ -201,18 +126,117 @@ void Reducer::AddDefaultReductionPasses() {
|
||||
ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
|
||||
|
||||
// Cleanup passes.
|
||||
|
||||
AddCleanupReductionPass(
|
||||
spvtools::MakeUnique<
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder>(true));
|
||||
}
|
||||
|
||||
void Reducer::AddReductionPass(
|
||||
std::unique_ptr<ReductionOpportunityFinder>&& finder) {
|
||||
impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
|
||||
impl_->target_env, std::move(finder)));
|
||||
passes_.push_back(
|
||||
spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
|
||||
}
|
||||
|
||||
bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,
|
||||
spv_const_reducer_options options) {
|
||||
void Reducer::AddCleanupReductionPass(
|
||||
std::unique_ptr<ReductionOpportunityFinder>&& finder) {
|
||||
cleanup_passes_.push_back(
|
||||
spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
|
||||
}
|
||||
|
||||
bool Reducer::ReachedStepLimit(uint32_t current_step,
|
||||
spv_const_reducer_options options) {
|
||||
return current_step >= options->step_limit;
|
||||
}
|
||||
|
||||
Reducer::ReductionResultStatus Reducer::RunPasses(
|
||||
std::vector<std::unique_ptr<ReductionPass>>* passes,
|
||||
spv_const_reducer_options options, spv_validator_options validator_options,
|
||||
const SpirvTools& tools, std::vector<uint32_t>* current_binary,
|
||||
uint32_t* const reductions_applied) {
|
||||
// Determines whether, on completing one round of reduction passes, it is
|
||||
// worthwhile trying a further round.
|
||||
bool another_round_worthwhile = true;
|
||||
|
||||
// Apply round after round of reduction passes until we hit the reduction
|
||||
// step limit, or deem that another round is not going to be worthwhile.
|
||||
while (!ReachedStepLimit(*reductions_applied, options) &&
|
||||
another_round_worthwhile) {
|
||||
// At the start of a round of reduction passes, assume another round will
|
||||
// not be worthwhile unless we find evidence to the contrary.
|
||||
another_round_worthwhile = false;
|
||||
|
||||
// Iterate through the available passes.
|
||||
for (auto& pass : *passes) {
|
||||
// If this pass hasn't reached its minimum granularity then it's
|
||||
// worth eventually doing another round of reductions, in order to
|
||||
// try this pass at a finer granularity.
|
||||
another_round_worthwhile |= !pass->ReachedMinimumGranularity();
|
||||
|
||||
// Keep applying this pass at its current granularity until it stops
|
||||
// working or we hit the reduction step limit.
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
("Trying pass " + pass->GetName() + ".").c_str());
|
||||
do {
|
||||
auto maybe_result = pass->TryApplyReduction(*current_binary);
|
||||
if (maybe_result.empty()) {
|
||||
// For this round, the pass has no more opportunities (chunks) to
|
||||
// apply, so move on to the next pass.
|
||||
consumer_(
|
||||
SPV_MSG_INFO, nullptr, {},
|
||||
("Pass " + pass->GetName() + " did not make a reduction step.")
|
||||
.c_str());
|
||||
break;
|
||||
}
|
||||
bool interesting = false;
|
||||
std::stringstream stringstream;
|
||||
(*reductions_applied)++;
|
||||
stringstream << "Pass " << pass->GetName() << " made reduction step "
|
||||
<< *reductions_applied << ".";
|
||||
consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str()));
|
||||
if (!tools.Validate(&maybe_result[0], maybe_result.size(),
|
||||
validator_options)) {
|
||||
// The reduction step went wrong and an invalid binary was produced.
|
||||
// By design, this shouldn't happen; this is a safeguard to stop an
|
||||
// invalid binary from being regarded as interesting.
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Reduction step produced an invalid binary.");
|
||||
if (options->fail_on_validation_error) {
|
||||
// In this mode, we fail, so we update the current binary so it is
|
||||
// output for debugging.
|
||||
*current_binary = std::move(maybe_result);
|
||||
return Reducer::ReductionResultStatus::kStateInvalid;
|
||||
}
|
||||
} else if (interestingness_function_(maybe_result,
|
||||
*reductions_applied)) {
|
||||
// Success! The binary produced by this reduction step is
|
||||
// interesting, so make it the binary of interest henceforth, and
|
||||
// note that it's worth doing another round of reduction passes.
|
||||
consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded.");
|
||||
*current_binary = std::move(maybe_result);
|
||||
interesting = true;
|
||||
another_round_worthwhile = true;
|
||||
}
|
||||
// We must call this before the next call to TryApplyReduction.
|
||||
pass->NotifyInteresting(interesting);
|
||||
// Bail out if the reduction step limit has been reached.
|
||||
} while (!ReachedStepLimit(*reductions_applied, options));
|
||||
}
|
||||
}
|
||||
|
||||
// Report whether reduction completed, or bailed out early due to reaching
|
||||
// the step limit.
|
||||
if (ReachedStepLimit(*reductions_applied, options)) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Reached reduction step limit; stopping.");
|
||||
return Reducer::ReductionResultStatus::kReachedStepLimit;
|
||||
}
|
||||
|
||||
// The passes completed successfully, although we may still run more passes.
|
||||
return Reducer::ReductionResultStatus::kComplete;
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
27
3rdparty/spirv-tools/source/reduce/reducer.h
vendored
27
3rdparty/spirv-tools/source/reduce/reducer.h
vendored
@@ -50,7 +50,7 @@ class Reducer {
|
||||
using InterestingnessFunction =
|
||||
std::function<bool(const std::vector<uint32_t>&, uint32_t)>;
|
||||
|
||||
// Constructs an instance with the given target |env|, which is used to
|
||||
// Constructs an instance with the given target |target_env|, which is used to
|
||||
// decode the binary to be reduced later.
|
||||
//
|
||||
// The constructed instance will have an empty message consumer, which just
|
||||
@@ -59,7 +59,7 @@ class Reducer {
|
||||
//
|
||||
// The constructed instance also needs to have an interestingness function
|
||||
// set and some reduction passes added to it in order to be useful.
|
||||
explicit Reducer(spv_target_env env);
|
||||
explicit Reducer(spv_target_env target_env);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
Reducer(const Reducer&) = delete;
|
||||
@@ -86,17 +86,34 @@ class Reducer {
|
||||
// that will be iterated over.
|
||||
void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
|
||||
|
||||
// Adds a cleanup reduction pass based on the given finder to the sequence of
|
||||
// passes that will run after other passes.
|
||||
void AddCleanupReductionPass(
|
||||
std::unique_ptr<ReductionOpportunityFinder>&& finder);
|
||||
|
||||
// Reduces the given SPIR-V module |binary_out|.
|
||||
// The reduced binary ends up in |binary_out|.
|
||||
// A status is returned.
|
||||
ReductionResultStatus Run(std::vector<uint32_t>&& binary_in,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
spv_const_reducer_options options,
|
||||
spv_validator_options validator_options) const;
|
||||
spv_validator_options validator_options);
|
||||
|
||||
private:
|
||||
struct Impl; // Opaque struct for holding internal data.
|
||||
std::unique_ptr<Impl> impl_; // Unique pointer to internal data.
|
||||
static bool ReachedStepLimit(uint32_t current_step,
|
||||
spv_const_reducer_options options);
|
||||
|
||||
ReductionResultStatus RunPasses(
|
||||
std::vector<std::unique_ptr<ReductionPass>>* passes,
|
||||
spv_const_reducer_options options,
|
||||
spv_validator_options validator_options, const SpirvTools& tools,
|
||||
std::vector<uint32_t>* current_binary, uint32_t* reductions_applied);
|
||||
|
||||
const spv_target_env target_env_;
|
||||
MessageConsumer consumer_;
|
||||
InterestingnessFunction interestingness_function_;
|
||||
std::vector<std::unique_ptr<ReductionPass>> passes_;
|
||||
std::vector<std::unique_ptr<ReductionPass>> cleanup_passes_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
|
||||
@@ -44,5 +44,22 @@ uint32_t FindOrCreateGlobalUndef(IRContext* context, uint32_t type_id) {
|
||||
return undef_id;
|
||||
}
|
||||
|
||||
void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
|
||||
opt::BasicBlock* to_block) {
|
||||
to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) {
|
||||
Instruction::OperandList new_in_operands;
|
||||
// Go through the OpPhi's input operands in (variable, parent) pairs.
|
||||
for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) {
|
||||
// Keep all pairs where the parent is not the block from which the edge
|
||||
// is being removed.
|
||||
if (phi_inst->GetInOperand(index + 1).words[0] != from_id) {
|
||||
new_in_operands.push_back(phi_inst->GetInOperand(index));
|
||||
new_in_operands.push_back(phi_inst->GetInOperand(index + 1));
|
||||
}
|
||||
}
|
||||
phi_inst->SetInOperands(std::move(new_in_operands));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -30,6 +30,11 @@ extern const uint32_t kFalseBranchOperandIndex;
|
||||
// adding one if it does not exist.
|
||||
uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id);
|
||||
|
||||
// Removes any components of |to_block|'s phi instructions relating to
|
||||
// |from_id|.
|
||||
void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
|
||||
opt::BasicBlock* to_block);
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
|
||||
|
||||
#include "source/opcode.h"
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/reduce/remove_instruction_reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::IRContext;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
RemoveOpNameInstructionReductionOpportunityFinder::GetAvailableOpportunities(
|
||||
IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
for (auto& inst : context->module()->debugs2()) {
|
||||
if (inst.opcode() == SpvOpName || inst.opcode() == SpvOpMemberName) {
|
||||
result.push_back(
|
||||
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string RemoveOpNameInstructionReductionOpportunityFinder::GetName() const {
|
||||
return "RemoveOpNameInstructionReductionOpportunityFinder";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2018 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_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
|
||||
#define SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
|
||||
|
||||
#include "source/reduce/reduction_opportunity_finder.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A finder for opportunities to remove OpName instructions. As well as making
|
||||
// the module smaller, removing an OpName instruction may create opportunities
|
||||
// for subsequently removing the instructions that create the ids to which the
|
||||
// OpName applies.
|
||||
class RemoveOpNameInstructionReductionOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
public:
|
||||
RemoveOpNameInstructionReductionOpportunityFinder() = default;
|
||||
|
||||
~RemoveOpNameInstructionReductionOpportunityFinder() override = default;
|
||||
|
||||
std::string GetName() const final;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const final;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// 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/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
|
||||
|
||||
#include "source/reduce/remove_instruction_reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
|
||||
opt::IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
// Consider all annotation instructions
|
||||
for (auto& inst : context->module()->annotations()) {
|
||||
// We are interested in removing instructions of the form:
|
||||
// SpvOpDecorate %id RelaxedPrecision
|
||||
// and
|
||||
// SpvOpMemberDecorate %id member RelaxedPrecision
|
||||
if ((inst.opcode() == SpvOpDecorate &&
|
||||
inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
|
||||
(inst.opcode() == SpvOpMemberDecorate &&
|
||||
inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
|
||||
result.push_back(
|
||||
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
|
||||
return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
@@ -21,28 +21,109 @@
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder::
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder(
|
||||
bool remove_constants_and_undefs)
|
||||
: remove_constants_and_undefs_(remove_constants_and_undefs) {}
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder::
|
||||
GetAvailableOpportunities(opt::IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
for (auto& inst : context->module()->debugs1()) {
|
||||
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
|
||||
for (auto& inst : context->module()->debugs2()) {
|
||||
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
|
||||
for (auto& inst : context->module()->debugs3()) {
|
||||
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
|
||||
for (auto& inst : context->module()->types_values()) {
|
||||
if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
|
||||
continue;
|
||||
}
|
||||
if (!remove_constants_and_undefs_ &&
|
||||
spvOpcodeIsConstantOrUndef(inst.opcode())) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
|
||||
for (auto& inst : context->module()->annotations()) {
|
||||
if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t decoration = SpvDecorationMax;
|
||||
switch (inst.opcode()) {
|
||||
case SpvOpDecorate:
|
||||
case SpvOpDecorateId:
|
||||
case SpvOpDecorateString:
|
||||
decoration = inst.GetSingleWordInOperand(1u);
|
||||
break;
|
||||
case SpvOpMemberDecorate:
|
||||
case SpvOpMemberDecorateString:
|
||||
decoration = inst.GetSingleWordInOperand(2u);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// We conservatively only remove specific decorations that we believe will
|
||||
// not change the shader interface, will not make the shader invalid, will
|
||||
// actually be found in practice, etc.
|
||||
|
||||
switch (decoration) {
|
||||
case SpvDecorationRelaxedPrecision:
|
||||
case SpvDecorationNoSignedWrap:
|
||||
case SpvDecorationNoContraction:
|
||||
case SpvDecorationNoUnsignedWrap:
|
||||
case SpvDecorationUserSemantic:
|
||||
break;
|
||||
default:
|
||||
// Give up.
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
|
||||
for (auto& function : *context->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& inst : block) {
|
||||
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
|
||||
continue;
|
||||
}
|
||||
if (!remove_constants_and_undefs_ &&
|
||||
spvOpcodeIsConstantOrUndef(inst.opcode())) {
|
||||
continue;
|
||||
}
|
||||
if (spvOpcodeIsBlockTerminator(inst.opcode()) ||
|
||||
inst.opcode() == SpvOpSelectionMerge ||
|
||||
inst.opcode() == SpvOpLoopMerge) {
|
||||
// In this reduction pass we do not want to affect static control
|
||||
// flow.
|
||||
// In this reduction pass we do not want to affect static
|
||||
// control flow.
|
||||
continue;
|
||||
}
|
||||
// Given that we're in a block, we should only get here if the
|
||||
// instruction is not directly related to control flow; i.e., it's
|
||||
// some straightforward instruction with an unused result, like an
|
||||
// arithmetic operation or function call.
|
||||
// Given that we're in a block, we should only get here if
|
||||
// the instruction is not directly related to control flow;
|
||||
// i.e., it's some straightforward instruction with an
|
||||
// unused result, like an arithmetic operation or function
|
||||
// call.
|
||||
result.push_back(
|
||||
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ namespace reduce {
|
||||
class RemoveUnreferencedInstructionReductionOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
public:
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder() = default;
|
||||
explicit RemoveUnreferencedInstructionReductionOpportunityFinder(
|
||||
bool remove_constants_and_undefs);
|
||||
|
||||
~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
|
||||
|
||||
@@ -38,6 +39,7 @@ class RemoveUnreferencedInstructionReductionOpportunityFinder
|
||||
opt::IRContext* context) const final;
|
||||
|
||||
private:
|
||||
bool remove_constants_and_undefs_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
|
||||
@@ -167,23 +167,6 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectEdge(
|
||||
context_->cfg()->block(new_target_id));
|
||||
}
|
||||
|
||||
void StructuredLoopToSelectionReductionOpportunity::
|
||||
AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, BasicBlock* to_block) {
|
||||
to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) {
|
||||
Instruction::OperandList new_in_operands;
|
||||
// Go through the OpPhi's input operands in (variable, parent) pairs.
|
||||
for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) {
|
||||
// Keep all pairs where the parent is not the block from which the edge
|
||||
// is being removed.
|
||||
if (phi_inst->GetInOperand(index + 1).words[0] != from_id) {
|
||||
new_in_operands.push_back(phi_inst->GetInOperand(index));
|
||||
new_in_operands.push_back(phi_inst->GetInOperand(index + 1));
|
||||
}
|
||||
}
|
||||
phi_inst->SetInOperands(std::move(new_in_operands));
|
||||
});
|
||||
}
|
||||
|
||||
void StructuredLoopToSelectionReductionOpportunity::
|
||||
AdaptPhiInstructionsForAddedEdge(uint32_t from_id, BasicBlock* to_block) {
|
||||
to_block->ForEachPhiInst([this, &from_id](Instruction* phi_inst) {
|
||||
|
||||
@@ -62,11 +62,6 @@ class StructuredLoopToSelectionReductionOpportunity
|
||||
void RedirectEdge(uint32_t source_id, uint32_t original_target_id,
|
||||
uint32_t new_target_id);
|
||||
|
||||
// Removes any components of |to_block|'s phi instructions relating to
|
||||
// |from_id|.
|
||||
void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
|
||||
opt::BasicBlock* to_block);
|
||||
|
||||
// Adds components to |to_block|'s phi instructions to account for a new
|
||||
// incoming edge from |from_id|.
|
||||
void AdaptPhiInstructionsForAddedEdge(uint32_t from_id,
|
||||
|
||||
68
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
68
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
@@ -590,9 +590,68 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Validates that all CFG divergences (i.e. conditional branch or switch) are
|
||||
// structured correctly. Either divergence is preceded by a merge instruction
|
||||
// or the divergence introduces at most one unseen label.
|
||||
spv_result_t ValidateStructuredSelections(
|
||||
ValidationState_t& _, const std::vector<const BasicBlock*>& postorder) {
|
||||
std::unordered_set<uint32_t> seen;
|
||||
for (auto iter = postorder.rbegin(); iter != postorder.rend(); ++iter) {
|
||||
const auto* block = *iter;
|
||||
const auto* terminator = block->terminator();
|
||||
if (!terminator) continue;
|
||||
const auto index = terminator - &_.ordered_instructions()[0];
|
||||
auto* merge = &_.ordered_instructions()[index - 1];
|
||||
// Marks merges and continues as seen.
|
||||
if (merge->opcode() == SpvOpSelectionMerge) {
|
||||
seen.insert(merge->GetOperandAs<uint32_t>(0));
|
||||
} else if (merge->opcode() == SpvOpLoopMerge) {
|
||||
seen.insert(merge->GetOperandAs<uint32_t>(0));
|
||||
seen.insert(merge->GetOperandAs<uint32_t>(1));
|
||||
} else {
|
||||
// Only track the pointer if it is a merge instruction.
|
||||
merge = nullptr;
|
||||
}
|
||||
|
||||
// Skip unreachable blocks.
|
||||
if (!block->reachable()) continue;
|
||||
|
||||
if (terminator->opcode() == SpvOpBranchConditional) {
|
||||
const auto true_label = terminator->GetOperandAs<uint32_t>(1);
|
||||
const auto false_label = terminator->GetOperandAs<uint32_t>(2);
|
||||
// Mark the upcoming blocks as seen now, but only error out if this block
|
||||
// was missing a merge instruction and both labels hadn't been seen
|
||||
// previously.
|
||||
const bool both_unseen =
|
||||
seen.insert(true_label).second && seen.insert(false_label).second;
|
||||
if (!merge && both_unseen) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, terminator)
|
||||
<< "Selection must be structured";
|
||||
}
|
||||
} else if (terminator->opcode() == SpvOpSwitch) {
|
||||
uint32_t count = 0;
|
||||
// Mark the targets as seen now, but only error out if this block was
|
||||
// missing a merge instruction and there were multiple unseen labels.
|
||||
for (uint32_t i = 1; i < terminator->operands().size(); i += 2) {
|
||||
const auto target = terminator->GetOperandAs<uint32_t>(i);
|
||||
if (seen.insert(target).second) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (!merge && count > 1) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, terminator)
|
||||
<< "Selection must be structured";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t StructuredControlFlowChecks(
|
||||
ValidationState_t& _, Function* function,
|
||||
const std::vector<std::pair<uint32_t, uint32_t>>& back_edges) {
|
||||
const std::vector<std::pair<uint32_t, uint32_t>>& back_edges,
|
||||
const std::vector<const BasicBlock*>& postorder) {
|
||||
/// Check all backedges target only loop headers and have exactly one
|
||||
/// back-edge branching to it
|
||||
|
||||
@@ -709,6 +768,10 @@ spv_result_t StructuredControlFlowChecks(
|
||||
}
|
||||
}
|
||||
|
||||
if (auto error = ValidateStructuredSelections(_, postorder)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -931,7 +994,8 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
|
||||
|
||||
/// Structured control flow checks are only required for shader capabilities
|
||||
if (_.HasCapability(SpvCapabilityShader)) {
|
||||
if (auto error = StructuredControlFlowChecks(_, &function, back_edges))
|
||||
if (auto error =
|
||||
StructuredControlFlowChecks(_, &function, back_edges, postorder))
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,13 @@ spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
spv_result_t ValidateShaderClock(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
// #2952: disabled until scope discussion is resolved.
|
||||
#if 0
|
||||
const uint32_t execution_scope = inst->word(3);
|
||||
if (auto error = ValidateExecutionScope(_, inst, execution_scope)) {
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Result Type must be a 64 - bit unsigned integer type or
|
||||
// a vector of two - components of 32 -
|
||||
|
||||
@@ -24,15 +24,20 @@ if (${SPIRV_BUILD_FUZZER})
|
||||
transformation_add_constant_scalar_test.cpp
|
||||
transformation_add_dead_break_test.cpp
|
||||
transformation_add_dead_continue_test.cpp
|
||||
transformation_add_no_contraction_decoration_test.cpp
|
||||
transformation_add_type_boolean_test.cpp
|
||||
transformation_add_type_float_test.cpp
|
||||
transformation_add_type_int_test.cpp
|
||||
transformation_add_type_pointer_test.cpp
|
||||
transformation_construct_composite_test.cpp
|
||||
transformation_copy_object_test.cpp
|
||||
transformation_move_block_down_test.cpp
|
||||
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_function_control_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)
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
const uint32_t kNumFuzzerRuns = 20;
|
||||
|
||||
// Assembles the given |shader| text, and then runs the fuzzer |num_runs|
|
||||
// times, using successive seeds starting from |initial_seed|. Checks that
|
||||
// the binary produced after each fuzzer run is valid, and that replaying
|
||||
@@ -29,7 +31,7 @@ namespace {
|
||||
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);
|
||||
@@ -240,9 +242,9 @@ TEST(FuzzerReplayerTest, Miscellaneous1) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
// Do 5 fuzzer runs, starting from an initial seed of 0 (seed value chosen
|
||||
// Do some fuzzer runs, starting from an initial seed of 0 (seed value chosen
|
||||
// arbitrarily).
|
||||
RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, 5);
|
||||
RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, kNumFuzzerRuns);
|
||||
}
|
||||
|
||||
TEST(FuzzerReplayerTest, Miscellaneous2) {
|
||||
@@ -303,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"
|
||||
@@ -485,9 +487,9 @@ TEST(FuzzerReplayerTest, Miscellaneous2) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
// Do 5 fuzzer runs, starting from an initial seed of 10 (seed value chosen
|
||||
// Do some fuzzer runs, starting from an initial seed of 10 (seed value chosen
|
||||
// arbitrarily).
|
||||
RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, 5);
|
||||
RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, kNumFuzzerRuns);
|
||||
}
|
||||
|
||||
TEST(FuzzerReplayerTest, Miscellaneous3) {
|
||||
@@ -603,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"
|
||||
@@ -970,9 +972,9 @@ TEST(FuzzerReplayerTest, Miscellaneous3) {
|
||||
*facts.mutable_fact()->Add() = temp;
|
||||
}
|
||||
|
||||
// Do 5 fuzzer runs, starting from an initial seed of 94 (seed value chosen
|
||||
// Do some fuzzer runs, starting from an initial seed of 94 (seed value chosen
|
||||
// arbitrarily).
|
||||
RunFuzzerAndReplayer(shader, facts, 94, 5);
|
||||
RunFuzzerAndReplayer(shader, facts, 94, kNumFuzzerRuns);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1626,6 +1626,52 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous5) {
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest, DISABLED_Miscellaneous6) {
|
||||
// A miscellaneous test that exposing a known bug in spirv-fuzz.
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): re-enable
|
||||
// this test as an when the known issue is fixed.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeBool
|
||||
%9 = OpConstantTrue %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %13 %12 None
|
||||
OpBranchConditional %9 %13 %11
|
||||
%11 = OpLabel
|
||||
%20 = OpCopyObject %6 %9
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %9 %10 %13
|
||||
%13 = OpLabel
|
||||
%21 = OpCopyObject %6 %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
auto bad_transformation = TransformationAddDeadContinue(10, true, {});
|
||||
|
||||
ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
194
3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
vendored
Normal file
194
3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
// 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_add_no_contraction_decoration.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) {
|
||||
// This is a simple transformation and this test handles the main cases.
|
||||
|
||||
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 "x"
|
||||
OpName %10 "y"
|
||||
OpName %14 "i"
|
||||
OpDecorate %32 NoContraction
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 0
|
||||
%11 = OpConstant %6 2
|
||||
%12 = OpTypeInt 32 1
|
||||
%13 = OpTypePointer Function %12
|
||||
%15 = OpConstant %12 0
|
||||
%22 = OpConstant %12 10
|
||||
%23 = OpTypeBool
|
||||
%31 = OpConstant %6 3.5999999
|
||||
%38 = OpConstant %12 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%14 = OpVariable %13 Function
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
OpStore %14 %15
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
OpLoopMerge %18 %19 None
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpLoad %12 %14
|
||||
%24 = OpSLessThan %23 %21 %22
|
||||
OpBranchConditional %24 %17 %18
|
||||
%17 = OpLabel
|
||||
%25 = OpLoad %6 %10
|
||||
%26 = OpLoad %6 %10
|
||||
%27 = OpFMul %6 %25 %26
|
||||
%28 = OpLoad %6 %8
|
||||
%29 = OpFAdd %6 %28 %27
|
||||
OpStore %8 %29
|
||||
%30 = OpLoad %6 %10
|
||||
%32 = OpFDiv %6 %30 %31
|
||||
OpStore %10 %32
|
||||
%33 = OpLoad %12 %14
|
||||
%34 = OpConvertSToF %6 %33
|
||||
%35 = OpLoad %6 %8
|
||||
%36 = OpFAdd %6 %35 %34
|
||||
OpStore %8 %36
|
||||
OpBranch %19
|
||||
%19 = OpLabel
|
||||
%37 = OpLoad %12 %14
|
||||
%39 = OpIAdd %12 %37 %38
|
||||
OpStore %14 %39
|
||||
OpBranch %16
|
||||
%18 = 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;
|
||||
|
||||
// Invalid: 200 is not an id
|
||||
ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable(
|
||||
context.get(), fact_manager));
|
||||
// Invalid: 17 is a block id
|
||||
ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable(
|
||||
context.get(), fact_manager));
|
||||
// Invalid: 24 is not arithmetic
|
||||
ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable(
|
||||
context.get(), fact_manager));
|
||||
|
||||
// It is valid to add NoContraction to each of these ids (and it's fine to
|
||||
// have duplicates of the decoration, in the case of 32).
|
||||
for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) {
|
||||
TransformationAddNoContractionDecoration transformation(result_id);
|
||||
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
|
||||
transformation.Apply(context.get(), &fact_manager);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
}
|
||||
|
||||
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"
|
||||
OpName %8 "x"
|
||||
OpName %10 "y"
|
||||
OpName %14 "i"
|
||||
OpDecorate %32 NoContraction
|
||||
OpDecorate %32 NoContraction
|
||||
OpDecorate %32 NoContraction
|
||||
OpDecorate %27 NoContraction
|
||||
OpDecorate %29 NoContraction
|
||||
OpDecorate %39 NoContraction
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 0
|
||||
%11 = OpConstant %6 2
|
||||
%12 = OpTypeInt 32 1
|
||||
%13 = OpTypePointer Function %12
|
||||
%15 = OpConstant %12 0
|
||||
%22 = OpConstant %12 10
|
||||
%23 = OpTypeBool
|
||||
%31 = OpConstant %6 3.5999999
|
||||
%38 = OpConstant %12 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%14 = OpVariable %13 Function
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
OpStore %14 %15
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
OpLoopMerge %18 %19 None
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpLoad %12 %14
|
||||
%24 = OpSLessThan %23 %21 %22
|
||||
OpBranchConditional %24 %17 %18
|
||||
%17 = OpLabel
|
||||
%25 = OpLoad %6 %10
|
||||
%26 = OpLoad %6 %10
|
||||
%27 = OpFMul %6 %25 %26
|
||||
%28 = OpLoad %6 %8
|
||||
%29 = OpFAdd %6 %28 %27
|
||||
OpStore %8 %29
|
||||
%30 = OpLoad %6 %10
|
||||
%32 = OpFDiv %6 %30 %31
|
||||
OpStore %10 %32
|
||||
%33 = OpLoad %12 %14
|
||||
%34 = OpConvertSToF %6 %33
|
||||
%35 = OpLoad %6 %8
|
||||
%36 = OpFAdd %6 %35 %34
|
||||
OpStore %8 %36
|
||||
OpBranch %19
|
||||
%19 = OpLabel
|
||||
%37 = OpLoad %12 %14
|
||||
%39 = OpIAdd %12 %37 %38
|
||||
OpStore %14 %39
|
||||
OpBranch %16
|
||||
%18 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
1248
3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp
vendored
Normal file
1248
3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -164,12 +165,14 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
|
||||
FactManager fact_manager;
|
||||
|
||||
std::vector<protobufs::IdUseDescriptor> uses_of_true = {
|
||||
transformation::MakeIdUseDescriptor(41, SpvOpStore, 1, 44, 12),
|
||||
transformation::MakeIdUseDescriptor(41, SpvOpLogicalOr, 0, 46, 0)};
|
||||
MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
|
||||
MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0),
|
||||
0)};
|
||||
|
||||
std::vector<protobufs::IdUseDescriptor> uses_of_false = {
|
||||
transformation::MakeIdUseDescriptor(43, SpvOpStore, 1, 44, 13),
|
||||
transformation::MakeIdUseDescriptor(43, SpvOpLogicalAnd, 1, 48, 0)};
|
||||
MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1),
|
||||
MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0),
|
||||
1)};
|
||||
|
||||
const uint32_t fresh_id = 100;
|
||||
|
||||
@@ -529,10 +532,10 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
auto use_of_true_in_if =
|
||||
transformation::MakeIdUseDescriptor(13, SpvOpBranchConditional, 0, 10, 0);
|
||||
auto use_of_false_in_while =
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpBranchConditional, 0, 16, 0);
|
||||
auto use_of_true_in_if = MakeIdUseDescriptor(
|
||||
13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
|
||||
auto use_of_false_in_while = MakeIdUseDescriptor(
|
||||
21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0);
|
||||
|
||||
auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
|
||||
use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
|
||||
@@ -641,8 +644,8 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) {
|
||||
FactManager fact_manager;
|
||||
|
||||
auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpPhi, 0, 23, 0), 13, 15,
|
||||
SpvOpSLessThan, 100);
|
||||
MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
|
||||
15, SpvOpSLessThan, 100);
|
||||
|
||||
ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
@@ -116,11 +117,11 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
|
||||
|
||||
// The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
|
||||
protobufs::IdUseDescriptor use_of_9_in_store =
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
|
||||
MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
|
||||
protobufs::IdUseDescriptor use_of_11_in_add =
|
||||
transformation::MakeIdUseDescriptor(11, SpvOpIAdd, 1, 12, 0);
|
||||
MakeIdUseDescriptor(11, MakeInstructionDescriptor(12, SpvOpIAdd, 0), 1);
|
||||
protobufs::IdUseDescriptor use_of_14_in_add =
|
||||
transformation::MakeIdUseDescriptor(14, SpvOpIAdd, 0, 15, 0);
|
||||
MakeIdUseDescriptor(14, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
|
||||
|
||||
// These transformations work: they match the facts.
|
||||
auto transformation_use_of_9_in_store =
|
||||
@@ -167,7 +168,7 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
|
||||
// The following transformation does not apply because the id descriptor is
|
||||
// not sensible.
|
||||
protobufs::IdUseDescriptor nonsense_id_use_descriptor =
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0);
|
||||
MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
|
||||
ASSERT_FALSE(TransformationReplaceConstantWithUniform(
|
||||
nonsense_id_use_descriptor, blockname_a, 101, 102)
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
@@ -477,13 +478,13 @@ TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) {
|
||||
|
||||
// The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
|
||||
protobufs::IdUseDescriptor use_of_13_in_store =
|
||||
transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 21, 0);
|
||||
MakeIdUseDescriptor(13, MakeInstructionDescriptor(21, SpvOpStore, 0), 1);
|
||||
protobufs::IdUseDescriptor use_of_15_in_add =
|
||||
transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 1, 16, 0);
|
||||
MakeIdUseDescriptor(15, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 1);
|
||||
protobufs::IdUseDescriptor use_of_17_in_add =
|
||||
transformation::MakeIdUseDescriptor(17, SpvOpIAdd, 0, 19, 0);
|
||||
MakeIdUseDescriptor(17, MakeInstructionDescriptor(19, SpvOpIAdd, 0), 0);
|
||||
protobufs::IdUseDescriptor use_of_20_in_store =
|
||||
transformation::MakeIdUseDescriptor(20, SpvOpStore, 1, 19, 1);
|
||||
MakeIdUseDescriptor(20, MakeInstructionDescriptor(19, SpvOpStore, 1), 1);
|
||||
|
||||
// These transformations work: they match the facts.
|
||||
auto transformation_use_of_13_in_store =
|
||||
@@ -703,7 +704,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) {
|
||||
|
||||
// The constant id is 9 for 0.
|
||||
protobufs::IdUseDescriptor use_of_9_in_store =
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
|
||||
MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
|
||||
|
||||
// This transformation is not available because no uniform pointer to integer
|
||||
// type is present:
|
||||
@@ -778,7 +779,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) {
|
||||
|
||||
// The constant id is 9 for 9.
|
||||
protobufs::IdUseDescriptor use_of_9_in_store =
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
|
||||
MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
|
||||
|
||||
// This transformation is not available because no constant is present for the
|
||||
// index 1 required to index into the uniform buffer:
|
||||
@@ -852,7 +853,7 @@ TEST(TransformationReplaceConstantWithUniformTest,
|
||||
|
||||
// The constant id is 9 for 3.0.
|
||||
protobufs::IdUseDescriptor use_of_9_in_store =
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
|
||||
MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
|
||||
|
||||
// This transformation is not available because no integer type is present to
|
||||
// allow a constant index to be expressed:
|
||||
@@ -937,9 +938,9 @@ TEST(TransformationReplaceConstantWithUniformTest,
|
||||
|
||||
// The constant ids for 9 and 10 are 9 and 11 respectively
|
||||
protobufs::IdUseDescriptor use_of_9_in_store =
|
||||
transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 10, 0);
|
||||
MakeIdUseDescriptor(9, MakeInstructionDescriptor(10, SpvOpStore, 0), 1);
|
||||
protobufs::IdUseDescriptor use_of_11_in_store =
|
||||
transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1);
|
||||
MakeIdUseDescriptor(11, MakeInstructionDescriptor(10, SpvOpStore, 1), 1);
|
||||
|
||||
// These are right:
|
||||
ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
|
||||
@@ -1220,58 +1221,58 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) {
|
||||
std::vector<TransformationReplaceConstantWithUniform> transformations;
|
||||
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0),
|
||||
MakeIdUseDescriptor(18, MakeInstructionDescriptor(20, SpvOpStore, 0), 1),
|
||||
uniform_f_a_4, 200, 201));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
|
||||
MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1),
|
||||
uniform_f_a_3, 202, 203));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0),
|
||||
MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpStore, 0), 1),
|
||||
uniform_f_a_2, 204, 205));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0),
|
||||
MakeIdUseDescriptor(28, MakeInstructionDescriptor(29, SpvOpStore, 0), 1),
|
||||
uniform_f_a_1, 206, 207));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0),
|
||||
MakeIdUseDescriptor(31, MakeInstructionDescriptor(32, SpvOpStore, 0), 1),
|
||||
uniform_f_a_0, 208, 209));
|
||||
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0),
|
||||
MakeIdUseDescriptor(30, MakeInstructionDescriptor(35, SpvOpStore, 0), 1),
|
||||
uniform_f_b_w, 210, 211));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0),
|
||||
MakeIdUseDescriptor(27, MakeInstructionDescriptor(37, SpvOpStore, 0), 1),
|
||||
uniform_f_b_z, 212, 213));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0),
|
||||
MakeIdUseDescriptor(24, MakeInstructionDescriptor(39, SpvOpStore, 0), 1),
|
||||
uniform_f_b_y, 214, 215));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0),
|
||||
MakeIdUseDescriptor(21, MakeInstructionDescriptor(41, SpvOpStore, 0), 1),
|
||||
uniform_f_b_x, 216, 217));
|
||||
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0),
|
||||
MakeIdUseDescriptor(44, MakeInstructionDescriptor(45, SpvOpStore, 0), 1),
|
||||
uniform_f_c_z, 220, 221));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0),
|
||||
MakeIdUseDescriptor(46, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
|
||||
uniform_f_c_y, 222, 223));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0),
|
||||
MakeIdUseDescriptor(48, MakeInstructionDescriptor(49, SpvOpStore, 0), 1),
|
||||
uniform_f_c_x, 224, 225));
|
||||
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0),
|
||||
MakeIdUseDescriptor(50, MakeInstructionDescriptor(52, SpvOpStore, 0), 1),
|
||||
uniform_f_d, 226, 227));
|
||||
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0),
|
||||
MakeIdUseDescriptor(53, MakeInstructionDescriptor(54, SpvOpStore, 0), 1),
|
||||
uniform_h_x, 228, 229));
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0),
|
||||
MakeIdUseDescriptor(55, MakeInstructionDescriptor(56, SpvOpStore, 0), 1),
|
||||
uniform_h_y, 230, 231));
|
||||
|
||||
transformations.emplace_back(TransformationReplaceConstantWithUniform(
|
||||
transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0), uniform_g,
|
||||
218, 219));
|
||||
MakeIdUseDescriptor(42, MakeInstructionDescriptor(43, SpvOpStore, 0), 1),
|
||||
uniform_g, 218, 219));
|
||||
|
||||
for (auto& transformation : transformations) {
|
||||
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
251
3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp
vendored
Normal file
251
3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
// 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_function_control.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
TEST(TransformationSetFunctionControlTest, VariousScenarios) {
|
||||
// This is a simple transformation; this test captures the important things
|
||||
// to check for.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %54
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %11 "foo(i1;i1;"
|
||||
OpName %9 "a"
|
||||
OpName %10 "b"
|
||||
OpName %13 "bar("
|
||||
OpName %17 "baz(i1;"
|
||||
OpName %16 "x"
|
||||
OpName %21 "boo(i1;i1;"
|
||||
OpName %19 "a"
|
||||
OpName %20 "b"
|
||||
OpName %29 "g"
|
||||
OpName %42 "param"
|
||||
OpName %44 "param"
|
||||
OpName %45 "param"
|
||||
OpName %48 "param"
|
||||
OpName %49 "param"
|
||||
OpName %54 "color"
|
||||
OpDecorate %54 Location 0
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%8 = OpTypeFunction %6 %7 %7
|
||||
%15 = OpTypeFunction %6 %7
|
||||
%28 = OpTypePointer Private %6
|
||||
%29 = OpVariable %28 Private
|
||||
%30 = OpConstant %6 2
|
||||
%31 = OpConstant %6 5
|
||||
%51 = OpTypeFloat 32
|
||||
%52 = OpTypeVector %51 4
|
||||
%53 = OpTypePointer Output %52
|
||||
%54 = OpVariable %53 Output
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%42 = OpVariable %7 Function
|
||||
%44 = OpVariable %7 Function
|
||||
%45 = OpVariable %7 Function
|
||||
%48 = OpVariable %7 Function
|
||||
%49 = OpVariable %7 Function
|
||||
%41 = OpFunctionCall %2 %13
|
||||
OpStore %42 %30
|
||||
%43 = OpFunctionCall %6 %17 %42
|
||||
OpStore %44 %31
|
||||
%46 = OpLoad %6 %29
|
||||
OpStore %45 %46
|
||||
%47 = OpFunctionCall %6 %21 %44 %45
|
||||
OpStore %48 %43
|
||||
OpStore %49 %47
|
||||
%50 = OpFunctionCall %6 %11 %48 %49
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%11 = OpFunction %6 Const %8
|
||||
%9 = OpFunctionParameter %7
|
||||
%10 = OpFunctionParameter %7
|
||||
%12 = OpLabel
|
||||
%23 = OpLoad %6 %9
|
||||
%24 = OpLoad %6 %10
|
||||
%25 = OpIAdd %6 %23 %24
|
||||
OpReturnValue %25
|
||||
OpFunctionEnd
|
||||
%13 = OpFunction %2 Inline %3
|
||||
%14 = OpLabel
|
||||
OpStore %29 %30
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%17 = OpFunction %6 Pure|DontInline %15
|
||||
%16 = OpFunctionParameter %7
|
||||
%18 = OpLabel
|
||||
%32 = OpLoad %6 %16
|
||||
%33 = OpIAdd %6 %31 %32
|
||||
OpReturnValue %33
|
||||
OpFunctionEnd
|
||||
%21 = OpFunction %6 DontInline %8
|
||||
%19 = OpFunctionParameter %7
|
||||
%20 = OpFunctionParameter %7
|
||||
%22 = OpLabel
|
||||
%36 = OpLoad %6 %19
|
||||
%37 = OpLoad %6 %20
|
||||
%38 = OpIMul %6 %36 %37
|
||||
OpReturnValue %38
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
// %36 is not a function
|
||||
ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone)
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
// Cannot add the Pure function control to %4 as it did not already have it
|
||||
ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask)
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
// Cannot add the Const function control to %21 as it did not already
|
||||
// have it
|
||||
ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask)
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
// Set to None, removing Const
|
||||
TransformationSetFunctionControl transformation1(11,
|
||||
SpvFunctionControlMaskNone);
|
||||
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
|
||||
transformation1.Apply(context.get(), &fact_manager);
|
||||
|
||||
// Set to Inline; silly to do it on an entry point, but it is allowed
|
||||
TransformationSetFunctionControl transformation2(
|
||||
4, SpvFunctionControlInlineMask);
|
||||
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
|
||||
transformation2.Apply(context.get(), &fact_manager);
|
||||
|
||||
// Set to Pure, removing DontInline
|
||||
TransformationSetFunctionControl transformation3(17,
|
||||
SpvFunctionControlPureMask);
|
||||
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
|
||||
transformation3.Apply(context.get(), &fact_manager);
|
||||
|
||||
// Change from Inline to DontInline
|
||||
TransformationSetFunctionControl transformation4(
|
||||
13, SpvFunctionControlDontInlineMask);
|
||||
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
|
||||
transformation4.Apply(context.get(), &fact_manager);
|
||||
|
||||
std::string after_transformation = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %54
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %11 "foo(i1;i1;"
|
||||
OpName %9 "a"
|
||||
OpName %10 "b"
|
||||
OpName %13 "bar("
|
||||
OpName %17 "baz(i1;"
|
||||
OpName %16 "x"
|
||||
OpName %21 "boo(i1;i1;"
|
||||
OpName %19 "a"
|
||||
OpName %20 "b"
|
||||
OpName %29 "g"
|
||||
OpName %42 "param"
|
||||
OpName %44 "param"
|
||||
OpName %45 "param"
|
||||
OpName %48 "param"
|
||||
OpName %49 "param"
|
||||
OpName %54 "color"
|
||||
OpDecorate %54 Location 0
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%8 = OpTypeFunction %6 %7 %7
|
||||
%15 = OpTypeFunction %6 %7
|
||||
%28 = OpTypePointer Private %6
|
||||
%29 = OpVariable %28 Private
|
||||
%30 = OpConstant %6 2
|
||||
%31 = OpConstant %6 5
|
||||
%51 = OpTypeFloat 32
|
||||
%52 = OpTypeVector %51 4
|
||||
%53 = OpTypePointer Output %52
|
||||
%54 = OpVariable %53 Output
|
||||
%4 = OpFunction %2 Inline %3
|
||||
%5 = OpLabel
|
||||
%42 = OpVariable %7 Function
|
||||
%44 = OpVariable %7 Function
|
||||
%45 = OpVariable %7 Function
|
||||
%48 = OpVariable %7 Function
|
||||
%49 = OpVariable %7 Function
|
||||
%41 = OpFunctionCall %2 %13
|
||||
OpStore %42 %30
|
||||
%43 = OpFunctionCall %6 %17 %42
|
||||
OpStore %44 %31
|
||||
%46 = OpLoad %6 %29
|
||||
OpStore %45 %46
|
||||
%47 = OpFunctionCall %6 %21 %44 %45
|
||||
OpStore %48 %43
|
||||
OpStore %49 %47
|
||||
%50 = OpFunctionCall %6 %11 %48 %49
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%11 = OpFunction %6 None %8
|
||||
%9 = OpFunctionParameter %7
|
||||
%10 = OpFunctionParameter %7
|
||||
%12 = OpLabel
|
||||
%23 = OpLoad %6 %9
|
||||
%24 = OpLoad %6 %10
|
||||
%25 = OpIAdd %6 %23 %24
|
||||
OpReturnValue %25
|
||||
OpFunctionEnd
|
||||
%13 = OpFunction %2 DontInline %3
|
||||
%14 = OpLabel
|
||||
OpStore %29 %30
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%17 = OpFunction %6 Pure %15
|
||||
%16 = OpFunctionParameter %7
|
||||
%18 = OpLabel
|
||||
%32 = OpLoad %6 %16
|
||||
%33 = OpIAdd %6 %31 %32
|
||||
OpReturnValue %33
|
||||
OpFunctionEnd
|
||||
%21 = OpFunction %6 DontInline %8
|
||||
%19 = OpFunctionParameter %7
|
||||
%20 = OpFunctionParameter %7
|
||||
%22 = OpLabel
|
||||
%36 = OpLoad %6 %19
|
||||
%37 = OpLoad %6 %20
|
||||
%38 = OpIMul %6 %36 %37
|
||||
OpReturnValue %38
|
||||
OpFunctionEnd
|
||||
)";
|
||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
968
3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp
vendored
Normal file
968
3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp
vendored
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
|
||||
219
3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp
vendored
Normal file
219
3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
// 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_selection_control.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
TEST(TransformationSetSelectionControlTest, VariousScenarios) {
|
||||
// This is a simple transformation; this test captures the important things
|
||||
// to check for.
|
||||
|
||||
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 3
|
||||
%25 = OpConstant %6 1
|
||||
%28 = OpConstant %6 2
|
||||
%38 = OpConstant %6 4
|
||||
%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
|
||||
%19 = OpLoad %6 %8
|
||||
%21 = OpSGreaterThan %17 %19 %20
|
||||
OpSelectionMerge %23 Flatten
|
||||
OpBranchConditional %21 %22 %23
|
||||
%22 = OpLabel
|
||||
%24 = OpLoad %6 %8
|
||||
%26 = OpIAdd %6 %24 %25
|
||||
OpStore %8 %26
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%27 = OpLoad %6 %8
|
||||
%29 = OpSLessThan %17 %27 %28
|
||||
OpSelectionMerge %31 DontFlatten
|
||||
OpBranchConditional %29 %30 %31
|
||||
%30 = OpLabel
|
||||
%32 = OpLoad %6 %8
|
||||
%33 = OpISub %6 %32 %25
|
||||
OpStore %8 %33
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%34 = OpLoad %6 %8
|
||||
OpSelectionMerge %37 None
|
||||
OpSwitch %34 %36 0 %35
|
||||
%36 = OpLabel
|
||||
OpBranch %37
|
||||
%35 = OpLabel
|
||||
%39 = OpLoad %6 %8
|
||||
%40 = OpIAdd %6 %39 %38
|
||||
OpStore %8 %40
|
||||
OpBranch %36
|
||||
%37 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%43 = OpLoad %6 %8
|
||||
%44 = OpIAdd %6 %43 %25
|
||||
OpStore %8 %44
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
|
||||
FactManager fact_manager;
|
||||
|
||||
// %44 is not a block
|
||||
ASSERT_FALSE(
|
||||
TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask)
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
// %13 does not end with OpSelectionMerge
|
||||
ASSERT_FALSE(
|
||||
TransformationSetSelectionControl(13, SpvSelectionControlMaskNone)
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
// %10 ends in OpLoopMerge, not OpSelectionMerge
|
||||
ASSERT_FALSE(
|
||||
TransformationSetSelectionControl(10, SpvSelectionControlMaskNone)
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
|
||||
TransformationSetSelectionControl transformation1(
|
||||
11, SpvSelectionControlDontFlattenMask);
|
||||
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
|
||||
transformation1.Apply(context.get(), &fact_manager);
|
||||
|
||||
TransformationSetSelectionControl transformation2(
|
||||
23, SpvSelectionControlFlattenMask);
|
||||
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
|
||||
transformation2.Apply(context.get(), &fact_manager);
|
||||
|
||||
TransformationSetSelectionControl transformation3(
|
||||
31, SpvSelectionControlMaskNone);
|
||||
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
|
||||
transformation3.Apply(context.get(), &fact_manager);
|
||||
|
||||
TransformationSetSelectionControl transformation4(
|
||||
31, SpvSelectionControlFlattenMask);
|
||||
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
|
||||
transformation4.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"
|
||||
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 3
|
||||
%25 = OpConstant %6 1
|
||||
%28 = OpConstant %6 2
|
||||
%38 = OpConstant %6 4
|
||||
%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
|
||||
%19 = OpLoad %6 %8
|
||||
%21 = OpSGreaterThan %17 %19 %20
|
||||
OpSelectionMerge %23 DontFlatten
|
||||
OpBranchConditional %21 %22 %23
|
||||
%22 = OpLabel
|
||||
%24 = OpLoad %6 %8
|
||||
%26 = OpIAdd %6 %24 %25
|
||||
OpStore %8 %26
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%27 = OpLoad %6 %8
|
||||
%29 = OpSLessThan %17 %27 %28
|
||||
OpSelectionMerge %31 Flatten
|
||||
OpBranchConditional %29 %30 %31
|
||||
%30 = OpLabel
|
||||
%32 = OpLoad %6 %8
|
||||
%33 = OpISub %6 %32 %25
|
||||
OpStore %8 %33
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%34 = OpLoad %6 %8
|
||||
OpSelectionMerge %37 Flatten
|
||||
OpSwitch %34 %36 0 %35
|
||||
%36 = OpLabel
|
||||
OpBranch %37
|
||||
%35 = OpLabel
|
||||
%39 = OpLoad %6 %8
|
||||
%40 = OpIAdd %6 %39 %38
|
||||
OpStore %8 %40
|
||||
OpBranch %36
|
||||
%37 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%43 = OpLoad %6 %8
|
||||
%44 = OpIAdd %6 %43 %25
|
||||
OpStore %8 %44
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -375,14 +375,12 @@ OpEntryPoint Vertex %1 "func" %2
|
||||
OpSelectionMerge %12 None
|
||||
OpBranchConditional %true %13 %12
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %true %14 %15
|
||||
OpBranchConditional %true %14 %12
|
||||
%14 = OpLabel
|
||||
OpBranch %12
|
||||
%15 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
%16 = OpPhi %uint %uint_0 %11 %uint_0 %14 %uint_1 %15
|
||||
OpStore %2 %16
|
||||
%15 = OpPhi %uint %uint_0 %11 %uint_0 %13 %uint_1 %14
|
||||
OpStore %2 %15
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -19,7 +21,6 @@
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/pass.h"
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
@@ -664,6 +665,90 @@ OpFunctionEnd)";
|
||||
EXPECT_EQ(next_id_bound, 0);
|
||||
EXPECT_EQ(current_bound, context->module()->id_bound());
|
||||
}
|
||||
|
||||
TEST_F(IRContextTest, CfgAndDomAnalysis) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%1 = OpTypeVoid
|
||||
%2 = OpTypeFunction %1
|
||||
%3 = OpFunction %1 None %2
|
||||
%4 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd)";
|
||||
|
||||
std::unique_ptr<IRContext> ctx =
|
||||
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
||||
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
// Building the dominator analysis should build the CFG.
|
||||
ASSERT_TRUE(ctx->module()->begin() != ctx->module()->end());
|
||||
ctx->GetDominatorAnalysis(&*ctx->module()->begin());
|
||||
|
||||
EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
|
||||
EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
|
||||
|
||||
// Invalidating the CFG analysis should invalidate the dominator analysis.
|
||||
ctx->InvalidateAnalyses(IRContext::kAnalysisCFG);
|
||||
EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
|
||||
EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
|
||||
}
|
||||
|
||||
TEST_F(IRContextTest, AsanErrorTest) {
|
||||
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 "x"
|
||||
OpName %10 "y"
|
||||
OpDecorate %8 RelaxedPrecision
|
||||
OpDecorate %10 RelaxedPrecision
|
||||
OpDecorate %11 RelaxedPrecision
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpPhi %6 %11 %5
|
||||
OpStore %10 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(
|
||||
env, consumer, shader, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
opt::Function* fun =
|
||||
context->cfg()->block(5)->GetParent(); // Computes the CFG analysis
|
||||
opt::DominatorAnalysis* dom = nullptr;
|
||||
dom = context->GetDominatorAnalysis(fun); // Computes the dominator analysis,
|
||||
// which depends on the CFG
|
||||
// analysis
|
||||
context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisDominatorAnalysis); // Invalidates the
|
||||
// CFG analysis
|
||||
dom = context->GetDominatorAnalysis(
|
||||
fun); // Recompute the CFG analysis because the Dominator tree uses it.
|
||||
auto bb = dom->ImmediateDominator(5);
|
||||
std::cout
|
||||
<< bb->id(); // Make sure asan does not complain about use after free.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -156,7 +156,8 @@ std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
|
||||
types.emplace_back(new ReserveId());
|
||||
types.emplace_back(new Queue());
|
||||
|
||||
// Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV
|
||||
// Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV,
|
||||
// CooperativeMatrixNV
|
||||
types.emplace_back(new Pipe(SpvAccessQualifierReadWrite));
|
||||
types.emplace_back(new Pipe(SpvAccessQualifierReadOnly));
|
||||
types.emplace_back(new ForwardPointer(1, SpvStorageClassInput));
|
||||
@@ -165,6 +166,7 @@ std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
|
||||
types.emplace_back(new PipeStorage());
|
||||
types.emplace_back(new NamedBarrier());
|
||||
types.emplace_back(new AccelerationStructureNV());
|
||||
types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24));
|
||||
|
||||
return types;
|
||||
}
|
||||
@@ -214,6 +216,7 @@ TEST(TypeManager, TypeStrings) {
|
||||
%arr_spec_const_with_id = OpTypeArray %s32 %spec_const_with_id
|
||||
%arr_long_constant = OpTypeArray %s32 %long_constant
|
||||
%arr_spec_const_op = OpTypeArray %s32 %spec_const_op
|
||||
%cm = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4
|
||||
)";
|
||||
|
||||
std::vector<std::pair<uint32_t, std::string>> type_id_strs = {
|
||||
@@ -251,6 +254,7 @@ TEST(TypeManager, TypeStrings) {
|
||||
{36, "[sint32, id(1), words(1,99,42)]"},
|
||||
{37, "[sint32, id(33), words(0,705032704,1)]"},
|
||||
{38, "[sint32, id(34), words(2,34)]"},
|
||||
{39, "<float64, 6, 6, 6>"},
|
||||
};
|
||||
|
||||
std::unique_ptr<IRContext> context =
|
||||
@@ -1060,6 +1064,7 @@ TEST(TypeManager, GetTypeInstructionAllTypes) {
|
||||
; CHECK: OpTypePipeStorage
|
||||
; CHECK: OpTypeNamedBarrier
|
||||
; CHECK: OpTypeAccelerationStructureNV
|
||||
; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]]
|
||||
OpCapability Shader
|
||||
OpCapability Int64
|
||||
OpCapability Linkage
|
||||
|
||||
@@ -23,8 +23,6 @@ add_spvtools_unittest(TARGET reduce
|
||||
reducer_test.cpp
|
||||
remove_block_test.cpp
|
||||
remove_function_test.cpp
|
||||
remove_opname_instruction_test.cpp
|
||||
remove_relaxed_precision_decoration_test.cpp
|
||||
remove_selection_test.cpp
|
||||
remove_unreferenced_instruction_test.cpp
|
||||
structured_loop_to_selection_test.cpp
|
||||
|
||||
255
3rdparty/spirv-tools/test/reduce/reducer_test.cpp
vendored
255
3rdparty/spirv-tools/test/reduce/reducer_test.cpp
vendored
@@ -14,8 +14,8 @@
|
||||
|
||||
#include "source/reduce/reducer.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
@@ -23,6 +23,12 @@ namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
using opt::BasicBlock;
|
||||
using opt::IRContext;
|
||||
|
||||
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
|
||||
const MessageConsumer kMessageConsumer = CLIMessageConsumer;
|
||||
|
||||
// This changes its mind each time IsInteresting is invoked as to whether the
|
||||
// binary is interesting, until some limit is reached after which the binary is
|
||||
// always deemed interesting. This is useful to test that reduction passes
|
||||
@@ -55,6 +61,8 @@ class PingPongInteresting {
|
||||
};
|
||||
|
||||
TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
// Check that ExprToConstant and RemoveUnreferenced work together; once some
|
||||
// ID uses have been changed to constants, those IDs can be removed.
|
||||
std::string original = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
@@ -149,15 +157,6 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %60
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %16 "buf2"
|
||||
OpMemberName %16 0 "i"
|
||||
OpName %18 ""
|
||||
OpName %25 "buf1"
|
||||
OpMemberName %25 0 "f"
|
||||
OpName %27 ""
|
||||
OpName %60 "_GLF_color"
|
||||
OpMemberDecorate %16 0 Offset 0
|
||||
OpDecorate %16 Block
|
||||
OpDecorate %18 DescriptorSet 0
|
||||
@@ -174,14 +173,12 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
%16 = OpTypeStruct %6
|
||||
%17 = OpTypePointer Uniform %16
|
||||
%18 = OpVariable %17 Uniform
|
||||
%19 = OpTypePointer Uniform %6
|
||||
%22 = OpTypeBool
|
||||
%100 = OpConstantTrue %22
|
||||
%24 = OpTypeFloat 32
|
||||
%25 = OpTypeStruct %24
|
||||
%26 = OpTypePointer Uniform %25
|
||||
%27 = OpVariable %26 Uniform
|
||||
%28 = OpTypePointer Uniform %24
|
||||
%31 = OpConstant %24 2
|
||||
%56 = OpConstant %6 1
|
||||
%58 = OpTypeVector %24 4
|
||||
@@ -209,8 +206,7 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||
Reducer reducer(env);
|
||||
Reducer reducer(kEnv);
|
||||
PingPongInteresting ping_pong_interesting(10);
|
||||
reducer.SetMessageConsumer(NopDiagnostic);
|
||||
reducer.SetInterestingnessFunction(
|
||||
@@ -218,12 +214,13 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
return ping_pong_interesting.IsInteresting(binary);
|
||||
});
|
||||
reducer.AddReductionPass(
|
||||
MakeUnique<OperandToConstReductionOpportunityFinder>());
|
||||
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>(
|
||||
false));
|
||||
reducer.AddReductionPass(
|
||||
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
|
||||
MakeUnique<OperandToConstReductionOpportunityFinder>());
|
||||
|
||||
std::vector<uint32_t> binary_in;
|
||||
SpirvTools t(env);
|
||||
SpirvTools t(kEnv);
|
||||
|
||||
ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
|
||||
std::vector<uint32_t> binary_out;
|
||||
@@ -237,71 +234,169 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
|
||||
ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
|
||||
|
||||
CheckEqual(env, expected, binary_out);
|
||||
CheckEqual(kEnv, expected, binary_out);
|
||||
}
|
||||
|
||||
TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
|
||||
const std::string original = R"(
|
||||
bool InterestingWhileOpcodeExists(const std::vector<uint32_t>& binary,
|
||||
uint32_t opcode, uint32_t count, bool dump) {
|
||||
if (dump) {
|
||||
std::stringstream ss;
|
||||
ss << "temp_" << count << ".spv";
|
||||
DumpShader(binary, ss.str().c_str());
|
||||
}
|
||||
|
||||
std::unique_ptr<IRContext> context =
|
||||
BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size());
|
||||
assert(context);
|
||||
bool interesting = false;
|
||||
for (auto& function : *context->module()) {
|
||||
context->cfg()->ForEachBlockInPostOrder(
|
||||
&*function.begin(), [opcode, &interesting](BasicBlock* block) -> void {
|
||||
for (auto& inst : *block) {
|
||||
if (inst.opcode() == opcode) {
|
||||
interesting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (interesting) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return interesting;
|
||||
}
|
||||
|
||||
bool InterestingWhileIMulReachable(const std::vector<uint32_t>& binary,
|
||||
uint32_t count) {
|
||||
return InterestingWhileOpcodeExists(binary, SpvOpIMul, count, false);
|
||||
}
|
||||
|
||||
bool InterestingWhileSDivReachable(const std::vector<uint32_t>& binary,
|
||||
uint32_t count) {
|
||||
return InterestingWhileOpcodeExists(binary, SpvOpSDiv, count, false);
|
||||
}
|
||||
|
||||
// The shader below was derived from the following GLSL, and optimized.
|
||||
// #version 310 es
|
||||
// precision highp float;
|
||||
// layout(location = 0) out vec4 _GLF_color;
|
||||
// int foo() {
|
||||
// int x = 1;
|
||||
// int y;
|
||||
// x = y / x; // SDiv
|
||||
// return x;
|
||||
// }
|
||||
// void main() {
|
||||
// int c;
|
||||
// while (bool(c)) {
|
||||
// do {
|
||||
// if (bool(c)) {
|
||||
// if (bool(c)) {
|
||||
// ++c;
|
||||
// } else {
|
||||
// _GLF_color.x = float(c*c); // IMul
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// } while(bool(foo()));
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
const std::string kShaderWithLoopsDivAndMul = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpEntryPoint Fragment %4 "main" %49
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
OpName %3 "a"
|
||||
OpName %4 "this-name-counts-as-usage-for-load-instruction"
|
||||
%5 = OpTypeVoid
|
||||
%6 = OpTypeFunction %5
|
||||
%7 = OpTypeFloat 32
|
||||
%8 = OpTypePointer Function %7
|
||||
%9 = OpConstant %7 1
|
||||
%2 = OpFunction %5 None %6
|
||||
%10 = OpLabel
|
||||
%3 = OpVariable %8 Function
|
||||
%4 = OpLoad %7 %3
|
||||
OpStore %3 %9
|
||||
OpName %4 "main"
|
||||
OpName %49 "_GLF_color"
|
||||
OpDecorate %49 Location 0
|
||||
OpDecorate %52 RelaxedPrecision
|
||||
OpDecorate %77 RelaxedPrecision
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%12 = OpConstant %6 1
|
||||
%27 = OpTypeBool
|
||||
%28 = OpTypeInt 32 0
|
||||
%29 = OpConstant %28 0
|
||||
%46 = OpTypeFloat 32
|
||||
%47 = OpTypeVector %46 4
|
||||
%48 = OpTypePointer Output %47
|
||||
%49 = OpVariable %48 Output
|
||||
%54 = OpTypePointer Output %46
|
||||
%64 = OpConstantFalse %27
|
||||
%67 = OpConstantTrue %27
|
||||
%81 = OpUndef %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %61
|
||||
%61 = OpLabel
|
||||
OpLoopMerge %60 %63 None
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%30 = OpINotEqual %27 %81 %29
|
||||
OpLoopMerge %22 %23 None
|
||||
OpBranchConditional %30 %21 %22
|
||||
%21 = OpLabel
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
OpLoopMerge %33 %38 None
|
||||
OpBranch %32
|
||||
%32 = OpLabel
|
||||
OpSelectionMerge %38 None
|
||||
OpBranchConditional %30 %37 %38
|
||||
%37 = OpLabel
|
||||
OpSelectionMerge %42 None
|
||||
OpBranchConditional %30 %41 %45
|
||||
%41 = OpLabel
|
||||
OpBranch %42
|
||||
%45 = OpLabel
|
||||
%52 = OpIMul %6 %81 %81
|
||||
%53 = OpConvertSToF %46 %52
|
||||
%55 = OpAccessChain %54 %49 %29
|
||||
OpStore %55 %53
|
||||
OpBranch %42
|
||||
%42 = OpLabel
|
||||
OpBranch %33
|
||||
%38 = OpLabel
|
||||
%77 = OpSDiv %6 %81 %12
|
||||
%58 = OpINotEqual %27 %77 %29
|
||||
OpBranchConditional %58 %31 %33
|
||||
%33 = OpLabel
|
||||
%86 = OpPhi %27 %67 %42 %64 %38
|
||||
OpSelectionMerge %68 None
|
||||
OpBranchConditional %86 %22 %68
|
||||
%68 = OpLabel
|
||||
OpBranch %22
|
||||
%23 = OpLabel
|
||||
OpBranch %20
|
||||
%22 = OpLabel
|
||||
%90 = OpPhi %27 %64 %20 %86 %33 %67 %68
|
||||
OpSelectionMerge %70 None
|
||||
OpBranchConditional %90 %60 %70
|
||||
%70 = OpLabel
|
||||
OpBranch %60
|
||||
%63 = OpLabel
|
||||
OpBranch %61
|
||||
%60 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string expected = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%5 = OpTypeVoid
|
||||
%6 = OpTypeFunction %5
|
||||
%7 = OpTypeFloat 32
|
||||
%8 = OpTypePointer Function %7
|
||||
%9 = OpConstant %7 1
|
||||
%2 = OpFunction %5 None %6
|
||||
%10 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
TEST(ReducerTest, ShaderReduceWhileMulReachable) {
|
||||
Reducer reducer(kEnv);
|
||||
|
||||
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||
Reducer reducer(env);
|
||||
// Make ping-pong interesting very quickly, as there are not many
|
||||
// opportunities.
|
||||
PingPongInteresting ping_pong_interesting(1);
|
||||
reducer.SetMessageConsumer(NopDiagnostic);
|
||||
reducer.SetInterestingnessFunction(
|
||||
[&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
|
||||
return ping_pong_interesting.IsInteresting(binary);
|
||||
});
|
||||
reducer.AddReductionPass(
|
||||
MakeUnique<RemoveOpNameInstructionReductionOpportunityFinder>());
|
||||
reducer.AddReductionPass(
|
||||
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
|
||||
reducer.SetInterestingnessFunction(InterestingWhileIMulReachable);
|
||||
reducer.AddDefaultReductionPasses();
|
||||
reducer.SetMessageConsumer(kMessageConsumer);
|
||||
|
||||
std::vector<uint32_t> binary_in;
|
||||
SpirvTools t(env);
|
||||
SpirvTools t(kEnv);
|
||||
|
||||
ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
|
||||
ASSERT_TRUE(
|
||||
t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
|
||||
std::vector<uint32_t> binary_out;
|
||||
spvtools::ReducerOptions reducer_options;
|
||||
reducer_options.set_step_limit(500);
|
||||
@@ -312,8 +407,30 @@ TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
|
||||
std::move(binary_in), &binary_out, reducer_options, validator_options);
|
||||
|
||||
ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
|
||||
}
|
||||
|
||||
CheckEqual(env, expected, binary_out);
|
||||
TEST(ReducerTest, ShaderReduceWhileDivReachable) {
|
||||
Reducer reducer(kEnv);
|
||||
|
||||
reducer.SetInterestingnessFunction(InterestingWhileSDivReachable);
|
||||
reducer.AddDefaultReductionPasses();
|
||||
reducer.SetMessageConsumer(kMessageConsumer);
|
||||
|
||||
std::vector<uint32_t> binary_in;
|
||||
SpirvTools t(kEnv);
|
||||
|
||||
ASSERT_TRUE(
|
||||
t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
|
||||
std::vector<uint32_t> binary_out;
|
||||
spvtools::ReducerOptions reducer_options;
|
||||
reducer_options.set_step_limit(500);
|
||||
reducer_options.set_fail_on_validation_error(true);
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
|
||||
Reducer::ReductionResultStatus status = reducer.Run(
|
||||
std::move(binary_in), &binary_out, reducer_options, validator_options);
|
||||
|
||||
ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
// Copyright (c) 2018 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/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_pass.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
TEST(RemoveOpnameInstructionReductionPassTest, NothingToRemove) {
|
||||
const std::string source = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, source, kReduceAssembleOption);
|
||||
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(RemoveOpnameInstructionReductionPassTest, RemoveSingleOpName) {
|
||||
const std::string prologue = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
)";
|
||||
|
||||
const std::string epilogue = R"(
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string original = prologue + R"(
|
||||
OpName %4 "main"
|
||||
)" + epilogue;
|
||||
|
||||
const std::string expected = prologue + epilogue;
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, original, kReduceAssembleOption);
|
||||
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(1, ops.size());
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
|
||||
CheckEqual(env, expected, context.get());
|
||||
}
|
||||
|
||||
TEST(RemoveOpnameInstructionReductionPassTest, TryApplyRemovesAllOpName) {
|
||||
const std::string prologue = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
)";
|
||||
|
||||
const std::string epilogue = R"(
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%11 = OpVariable %7 Function
|
||||
%12 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
OpStore %10 %9
|
||||
OpStore %11 %9
|
||||
OpStore %12 %9
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string original = prologue + R"(
|
||||
OpName %4 "main"
|
||||
OpName %8 "a"
|
||||
OpName %10 "b"
|
||||
OpName %11 "c"
|
||||
OpName %12 "d"
|
||||
)" + epilogue;
|
||||
|
||||
const std::string expected = prologue + epilogue;
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
{
|
||||
// Check the right number of opportunities is detected
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, original, kReduceAssembleOption);
|
||||
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(5, ops.size());
|
||||
}
|
||||
|
||||
{
|
||||
// The reduction should remove all OpName
|
||||
std::vector<uint32_t> binary;
|
||||
SpirvTools t(env);
|
||||
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
|
||||
auto reduced_binary =
|
||||
ReductionPass(env,
|
||||
spvtools::MakeUnique<
|
||||
RemoveOpNameInstructionReductionOpportunityFinder>())
|
||||
.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected, reduced_binary);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RemoveOpnameInstructionReductionPassTest,
|
||||
TryApplyRemovesAllOpNameAndOpMemberName) {
|
||||
const std::string prologue = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
)";
|
||||
|
||||
const std::string epilogue = R"(
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypeInt 32 1
|
||||
%8 = OpTypeVector %6 3
|
||||
%9 = OpTypeStruct %6 %7 %8
|
||||
%10 = OpTypePointer Function %9
|
||||
%12 = OpConstant %7 0
|
||||
%13 = OpConstant %6 1
|
||||
%14 = OpTypePointer Function %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%11 = OpVariable %10 Function
|
||||
%15 = OpAccessChain %14 %11 %12
|
||||
OpStore %15 %13
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string original = prologue + R"(
|
||||
OpName %4 "main"
|
||||
OpName %9 "S"
|
||||
OpMemberName %9 0 "f"
|
||||
OpMemberName %9 1 "i"
|
||||
OpMemberName %9 2 "v"
|
||||
OpName %11 "s"
|
||||
)" + epilogue;
|
||||
|
||||
const std::string expected = prologue + epilogue;
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
{
|
||||
// Check the right number of opportunities is detected
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, original, kReduceAssembleOption);
|
||||
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(6, ops.size());
|
||||
}
|
||||
|
||||
{
|
||||
// The reduction should remove all OpName
|
||||
std::vector<uint32_t> binary;
|
||||
SpirvTools t(env);
|
||||
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
|
||||
auto reduced_binary =
|
||||
ReductionPass(env,
|
||||
spvtools::MakeUnique<
|
||||
RemoveOpNameInstructionReductionOpportunityFinder>())
|
||||
.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected, reduced_binary);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
@@ -1,177 +0,0 @@
|
||||
// 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/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_pass.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
|
||||
const std::string source = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, source, kReduceAssembleOption);
|
||||
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
|
||||
const std::string source = 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 "f"
|
||||
OpName %12 "i"
|
||||
OpName %16 "v"
|
||||
OpName %19 "S"
|
||||
OpMemberName %19 0 "a"
|
||||
OpMemberName %19 1 "b"
|
||||
OpMemberName %19 2 "c"
|
||||
OpName %21 "s"
|
||||
OpDecorate %8 RelaxedPrecision
|
||||
OpDecorate %12 RelaxedPrecision
|
||||
OpDecorate %16 RelaxedPrecision
|
||||
OpDecorate %17 RelaxedPrecision
|
||||
OpDecorate %18 RelaxedPrecision
|
||||
OpMemberDecorate %19 0 RelaxedPrecision
|
||||
OpMemberDecorate %19 1 RelaxedPrecision
|
||||
OpMemberDecorate %19 2 RelaxedPrecision
|
||||
OpDecorate %22 RelaxedPrecision
|
||||
OpDecorate %23 RelaxedPrecision
|
||||
OpDecorate %24 RelaxedPrecision
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Function %10
|
||||
%13 = OpConstant %10 22
|
||||
%14 = OpTypeVector %6 2
|
||||
%15 = OpTypePointer Function %14
|
||||
%19 = OpTypeStruct %10 %6 %14
|
||||
%20 = OpTypePointer Function %19
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%12 = OpVariable %11 Function
|
||||
%16 = OpVariable %15 Function
|
||||
%21 = OpVariable %20 Function
|
||||
OpStore %8 %9
|
||||
OpStore %12 %13
|
||||
%17 = OpLoad %6 %8
|
||||
%18 = OpCompositeConstruct %14 %17 %17
|
||||
OpStore %16 %18
|
||||
%22 = OpLoad %10 %12
|
||||
%23 = OpLoad %6 %8
|
||||
%24 = OpLoad %14 %16
|
||||
%25 = OpCompositeConstruct %19 %22 %23 %24
|
||||
OpStore %21 %25
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, source, kReduceAssembleOption);
|
||||
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(11, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
}
|
||||
|
||||
const std::string expected = 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 "f"
|
||||
OpName %12 "i"
|
||||
OpName %16 "v"
|
||||
OpName %19 "S"
|
||||
OpMemberName %19 0 "a"
|
||||
OpMemberName %19 1 "b"
|
||||
OpMemberName %19 2 "c"
|
||||
OpName %21 "s"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Function %10
|
||||
%13 = OpConstant %10 22
|
||||
%14 = OpTypeVector %6 2
|
||||
%15 = OpTypePointer Function %14
|
||||
%19 = OpTypeStruct %10 %6 %14
|
||||
%20 = OpTypePointer Function %19
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%12 = OpVariable %11 Function
|
||||
%16 = OpVariable %15 Function
|
||||
%21 = OpVariable %20 Function
|
||||
OpStore %8 %9
|
||||
OpStore %12 %13
|
||||
%17 = OpLoad %6 %8
|
||||
%18 = OpCompositeConstruct %14 %17 %17
|
||||
OpStore %16 %18
|
||||
%22 = OpLoad %10 %12
|
||||
%23 = OpLoad %6 %8
|
||||
%24 = OpLoad %14 %16
|
||||
%25 = OpCompositeConstruct %19 %22 %23 %24
|
||||
OpStore %21 %25
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(env, expected, context.get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
@@ -23,19 +23,26 @@ namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
|
||||
const std::string prologue = R"(
|
||||
// A module with some unused instructions, including some unused OpStore
|
||||
// instructions.
|
||||
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
|
||||
|
||||
const std::string original = 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 "a"
|
||||
OpName %10 "b"
|
||||
OpName %12 "c"
|
||||
OpName %14 "d"
|
||||
OpSource ESSL 310 ; 0
|
||||
OpName %4 "main" ; 1
|
||||
OpName %8 "a" ; 2
|
||||
OpName %10 "b" ; 3
|
||||
OpName %12 "c" ; 4
|
||||
OpName %14 "d" ; 5
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
@@ -49,51 +56,323 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
|
||||
%10 = OpVariable %7 Function
|
||||
%12 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
OpStore %8 %9 ; 6
|
||||
OpStore %10 %11 ; 7
|
||||
OpStore %12 %13 ; 8
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15 ; 9
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
const std::string epilogue = R"(
|
||||
const MessageConsumer consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(kEnv, consumer, original, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(10, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
const std::string step_2 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 10 ; 0
|
||||
%11 = OpConstant %6 20 ; 1
|
||||
%13 = OpConstant %6 30 ; 2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function ; 3
|
||||
%12 = OpVariable %7 Function ; 4
|
||||
%14 = OpVariable %7 Function ; 5
|
||||
%15 = OpLoad %6 %8 ; 6
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string original = prologue + R"(
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
CheckEqual(kEnv, step_2, context.get());
|
||||
|
||||
const std::string expected_after_2 = prologue + R"(
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
const std::string expected_after_4 = prologue + R"(
|
||||
%15 = OpLoad %6 %8
|
||||
)" + epilogue;
|
||||
ASSERT_EQ(7, ops.size());
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, original, kReduceAssembleOption);
|
||||
const auto ops = RemoveUnreferencedInstructionReductionOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(4, ops.size());
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[1]->TryToApply();
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
CheckEqual(env, expected_after_2, context.get());
|
||||
const std::string step_3 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function ; 0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
ASSERT_TRUE(ops[2]->PreconditionHolds());
|
||||
ops[2]->TryToApply();
|
||||
ASSERT_TRUE(ops[3]->PreconditionHolds());
|
||||
ops[3]->TryToApply();
|
||||
CheckEqual(kEnv, step_3, context.get());
|
||||
|
||||
CheckEqual(env, expected_after_4, context.get());
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
const std::string step_4 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6 ; 0
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, step_4, context.get());
|
||||
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
const std::string step_5 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1 ; 0
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, step_5, context.get());
|
||||
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
const std::string step_6 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, step_6, context.get());
|
||||
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(RemoveUnreferencedInstructionReductionPassTest, Referenced) {
|
||||
// A module with some unused global variables, constants, and types. Some will
|
||||
// not be removed initially because of the OpDecorate instructions.
|
||||
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
|
||||
|
||||
const std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310 ; 1
|
||||
OpName %4 "main" ; 2
|
||||
OpName %12 "a" ; 3
|
||||
OpDecorate %12 RelaxedPrecision ; 4
|
||||
OpDecorate %13 RelaxedPrecision ; 5
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeBool
|
||||
%7 = OpConstantTrue %6 ; 6
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Private %10
|
||||
%12 = OpVariable %11 Private
|
||||
%13 = OpConstant %10 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(6, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeBool ; 1
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Private %10
|
||||
%12 = OpVariable %11 Private ; 2
|
||||
%13 = OpConstant %10 1 ; 3
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(3, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
std::string after_2 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%10 = OpTypeInt 32 1
|
||||
%11 = OpTypePointer Private %10 ; 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, after_2, context.get());
|
||||
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
std::string after_3 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%10 = OpTypeInt 32 1 ; 1
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, after_3, context.get());
|
||||
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
for (auto& op : ops) {
|
||||
ASSERT_TRUE(op->PreconditionHolds());
|
||||
op->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
}
|
||||
|
||||
std::string after_4 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, after_4, context.get());
|
||||
|
||||
ops = finder.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
352
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
352
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
@@ -3026,6 +3026,7 @@ OpMemoryModel Logical GLSL450
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSelectionMerge %block None
|
||||
OpBranchConditional %undef %block %unreachable
|
||||
%block = OpLabel
|
||||
OpReturn
|
||||
@@ -3049,6 +3050,7 @@ OpMemoryModel Logical GLSL450
|
||||
%undef = OpUndef %int
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSelectionMerge %block1 None
|
||||
OpSwitch %undef %block1 0 %unreachable 1 %block2
|
||||
%block1 = OpLabel
|
||||
OpReturn
|
||||
@@ -3748,7 +3750,355 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
/// TODO(umar): Nested CFG constructs
|
||||
TEST_F(ValidateCFG, MissingMergeConditionalBranchBad) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpBranchConditional %undef %then %else
|
||||
%then = OpLabel
|
||||
OpReturn
|
||||
%else = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeSwitchBad) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 0
|
||||
%undef = OpUndef %int
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSwitch %undef %then 0 %else
|
||||
%then = OpLabel
|
||||
OpReturn
|
||||
%else = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeSwitchBad2) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 0
|
||||
%undef = OpUndef %int
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSwitch %undef %then 0 %then 1 %then 2 %else
|
||||
%then = OpLabel
|
||||
OpReturn
|
||||
%else = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSelectionMerge %b3 None
|
||||
OpBranchConditional %undef %b1 %b2
|
||||
%b1 = OpLabel
|
||||
OpBranchConditional %undef %b2 %b3
|
||||
%b2 = OpLabel
|
||||
OpBranch %b3
|
||||
%b3 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpBranchConditional %undef %then %then
|
||||
%then = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeOneTargetSwitchGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 0
|
||||
%undef = OpUndef %int
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSwitch %undef %then 0 %then 1 %then
|
||||
%then = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 0
|
||||
%undef_int = OpUndef %int
|
||||
%bool = OpTypeBool
|
||||
%undef_bool = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSelectionMerge %merge None
|
||||
OpBranchConditional %undef_bool %merge %b1
|
||||
%b1 = OpLabel
|
||||
OpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2
|
||||
%b2 = OpLabel
|
||||
OpBranch %merge
|
||||
%merge = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpBranch %loop
|
||||
%loop = OpLabel
|
||||
OpLoopMerge %exit %continue None
|
||||
OpBranch %body
|
||||
%body = OpLabel
|
||||
OpBranchConditional %undef %body2 %exit
|
||||
%body2 = OpLabel
|
||||
OpBranch %continue
|
||||
%continue = OpLabel
|
||||
OpBranch %loop
|
||||
%exit = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeLoopContinueGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpBranch %loop
|
||||
%loop = OpLabel
|
||||
OpLoopMerge %exit %continue None
|
||||
OpBranch %body
|
||||
%body = OpLabel
|
||||
OpBranchConditional %undef %body2 %continue
|
||||
%body2 = OpLabel
|
||||
OpBranch %continue
|
||||
%continue = OpLabel
|
||||
OpBranch %loop
|
||||
%exit = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeSwitchBreakGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSelectionMerge %merge None
|
||||
OpSwitch %int_0 %merge 1 %b1
|
||||
%b1 = OpLabel
|
||||
OpBranchConditional %undef %merge %b2
|
||||
%b2 = OpLabel
|
||||
OpBranch %merge
|
||||
%merge = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSelectionMerge %merge None
|
||||
OpSwitch %int_0 %b1 1 %b2
|
||||
%b1 = OpLabel
|
||||
OpBranchConditional %undef %b3 %b2
|
||||
%b2 = OpLabel
|
||||
OpBranch %merge
|
||||
%b3 = OpLabel
|
||||
OpBranch %merge
|
||||
%merge = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeInALoopBad) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpBranch %loop
|
||||
%loop = OpLabel
|
||||
OpLoopMerge %exit %continue None
|
||||
OpBranch %body
|
||||
%body = OpLabel
|
||||
OpBranchConditional %undef %b1 %b2
|
||||
%b1 = OpLabel
|
||||
OpBranch %exit
|
||||
%b2 = OpLabel
|
||||
OpBranch %continue
|
||||
%continue = OpLabel
|
||||
OpBranch %loop
|
||||
%exit = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, MissingMergeCrissCrossBad) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpSelectionMerge %merge None
|
||||
OpBranchConditional %undef %b1 %b2
|
||||
%b1 = OpLabel
|
||||
OpBranchConditional %undef %b3 %b4
|
||||
%b2 = OpLabel
|
||||
OpBranchConditional %undef %b3 %b4
|
||||
%b3 = OpLabel
|
||||
OpBranch %merge
|
||||
%b4 = OpLabel
|
||||
OpBranch %merge
|
||||
%merge = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
|
||||
@@ -144,7 +144,8 @@ OpFunctionEnd)";
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("vector of two components"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMisc, ShaderClockExecutionScope) {
|
||||
// #2952: disabled until scope discussion is resolved.
|
||||
TEST_F(ValidateMisc, DISABLED_ShaderClockExecutionScope) {
|
||||
const std::string spirv = ShaderClockSpriv + R"(
|
||||
%3 = OpTypeFunction %void
|
||||
%ulong = OpTypeInt 64 0
|
||||
|
||||
Reference in New Issue
Block a user