From 521223b70a4dfd7bfd81224324a818bf890e3cfe Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 29 Jan 2020 15:52:31 +0000 Subject: [PATCH 01/88] spirv-fuzz: Make functions "livesafe" during donation (#3146) This change allows the generator to (optionally and at random) make the functions of a module "livesafe" during donation. This involves introducing a loop limiter variable to each function and gating the number of total loop iterations for the function using that variable. It also involves eliminating OpKill and OpUnreachable instructions (changing them to OpReturn/OpReturnValue), and clamping access chain indices so that they are always in-bounds. --- source/fuzz/fact_manager.cpp | 48 +- source/fuzz/fact_manager.h | 18 + source/fuzz/fuzzer_context.cpp | 5 + source/fuzz/fuzzer_context.h | 8 + source/fuzz/fuzzer_pass.cpp | 73 + source/fuzz/fuzzer_pass.h | 29 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 157 +- source/fuzz/fuzzer_pass_donate_modules.h | 13 +- source/fuzz/fuzzer_util.cpp | 12 +- source/fuzz/protobufs/spvtoolsfuzz.proto | 99 + source/fuzz/transformation_add_function.cpp | 753 +++++- source/fuzz/transformation_add_function.h | 64 +- .../fuzz/transformation_outline_function.cpp | 20 +- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 13 +- .../transformation_add_dead_break_test.cpp | 3 - .../fuzz/transformation_add_function_test.cpp | 2300 +++++++++++++++++ .../transformation_outline_function_test.cpp | 58 + 17 files changed, 3629 insertions(+), 44 deletions(-) diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp index 5c0814aab4..9672653a10 100644 --- a/source/fuzz/fact_manager.cpp +++ b/source/fuzz/fact_manager.cpp @@ -801,7 +801,7 @@ bool FactManager::DataSynonymFacts::IsSynonymous( //============================== //============================== -// Dead id facts +// Dead block facts // The purpose of this class is to group the fields and data used to represent // facts about data blocks. @@ -829,10 +829,41 @@ bool FactManager::DeadBlockFacts::BlockIsDead(uint32_t block_id) const { // End of dead block facts //============================== +//============================== +// Livesafe function facts + +// The purpose of this class is to group the fields and data used to represent +// facts about livesafe functions. +class FactManager::LivesafeFunctionFacts { + public: + // See method in FactManager which delegates to this method. + void AddFact(const protobufs::FactFunctionIsLivesafe& fact); + + // See method in FactManager which delegates to this method. + bool FunctionIsLivesafe(uint32_t function_id) const; + + private: + std::set livesafe_function_ids_; +}; + +void FactManager::LivesafeFunctionFacts::AddFact( + const protobufs::FactFunctionIsLivesafe& fact) { + livesafe_function_ids_.insert(fact.function_id()); +} + +bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe( + uint32_t function_id) const { + return livesafe_function_ids_.count(function_id) != 0; +} + +// End of livesafe function facts +//============================== + FactManager::FactManager() : uniform_constant_facts_(MakeUnique()), data_synonym_facts_(MakeUnique()), - dead_block_facts_(MakeUnique()) {} + dead_block_facts_(MakeUnique()), + livesafe_function_facts_(MakeUnique()) {} FactManager::~FactManager() = default; @@ -860,6 +891,9 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact, case protobufs::Fact::kBlockIsDeadFact: dead_block_facts_->AddFact(fact.block_is_dead_fact()); return true; + case protobufs::Fact::kFunctionIsLivesafeFact: + livesafe_function_facts_->AddFact(fact.function_is_livesafe_fact()); + return true; default: assert(false && "Unknown fact type."); return false; @@ -941,5 +975,15 @@ void FactManager::AddFactBlockIsDead(uint32_t block_id) { dead_block_facts_->AddFact(fact); } +bool FactManager::FunctionIsLivesafe(uint32_t function_id) const { + return livesafe_function_facts_->FunctionIsLivesafe(function_id); +} + +void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) { + protobufs::FactFunctionIsLivesafe fact; + fact.set_function_id(function_id); + livesafe_function_facts_->AddFact(fact); +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h index f035fcc052..20f270154f 100644 --- a/source/fuzz/fact_manager.h +++ b/source/fuzz/fact_manager.h @@ -61,6 +61,9 @@ class FactManager { // Records the fact that |block_id| is dead. void AddFactBlockIsDead(uint32_t block_id); + // Records the fact that |function_id| is livesafe. + void AddFactFunctionIsLivesafe(uint32_t function_id); + // The fact manager is responsible for managing a few distinct categories of // facts. In principle there could be different fact managers for each kind // of fact, but in practice providing one 'go to' place for facts is @@ -143,6 +146,16 @@ class FactManager { // End of dead block facts //============================== + //============================== + // Querying facts about livesafe function + + // Returns true if and ony if |function_id| is the id of a function known + // to be livesafe. + bool FunctionIsLivesafe(uint32_t function_id) const; + + // End of dead block facts + //============================== + private: // For each distinct kind of fact to be managed, we use a separate opaque // class type. @@ -159,6 +172,11 @@ class FactManager { class DeadBlockFacts; // Opaque class for management of dead block facts. std::unique_ptr dead_block_facts_; // Unique pointer to internal data. + + class LivesafeFunctionFacts; // Opaque class for management of livesafe + // function facts. + std::unique_ptr + livesafe_function_facts_; // Unique pointer to internal data. }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 559aecb356..afffcf54c8 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -38,6 +38,7 @@ const std::pair kChanceOfAdjustingSelectionControl = {20, const std::pair kChanceOfConstructingComposite = {20, 50}; const std::pair kChanceOfCopyingObject = {20, 50}; const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; +const std::pair kChanceOfMakingDonorLivesafe = {40, 60}; const std::pair kChanceOfMergingBlocks = {20, 95}; const std::pair kChanceOfMovingBlockDown = {20, 50}; const std::pair kChanceOfObfuscatingConstant = {10, 90}; @@ -49,6 +50,7 @@ const std::pair kChanceOfSplittingBlock = {40, 95}; // Keep them in alphabetical order. const uint32_t kDefaultMaxLoopControlPartialCount = 100; const uint32_t kDefaultMaxLoopControlPeelCount = 100; +const uint32_t kDefaultMaxLoopLimit = 20; // Default functions for controlling how deep to go during recursive // generation/transformation. Keep them in alphabetical order. @@ -89,6 +91,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); chance_of_donating_additional_module_ = ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule); + chance_of_making_donor_livesafe_ = + ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe); chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks); chance_of_moving_block_down_ = ChooseBetweenMinAndMax(kChanceOfMovingBlockDown); @@ -101,6 +105,7 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount; max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount; + max_loop_limit_ = kDefaultMaxLoopLimit; } FuzzerContext::~FuzzerContext() = default; diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 7cfe15b731..cc4337093d 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -85,6 +85,9 @@ class FuzzerContext { uint32_t GetChanceOfDonatingAdditionalModule() { return chance_of_donating_additional_module_; } + uint32_t ChanceOfMakingDonorLivesafe() { + return chance_of_making_donor_livesafe_; + } uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; } uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; } uint32_t GetChanceOfObfuscatingConstant() { @@ -103,6 +106,9 @@ class FuzzerContext { uint32_t GetRandomLoopControlPartialCount() { return random_generator_->RandomUint32(max_loop_control_partial_count_); } + uint32_t GetRandomLoopLimit() { + return random_generator_->RandomUint32(max_loop_limit_); + } // Functions to control how deeply to recurse. // Keep them in alphabetical order. @@ -129,6 +135,7 @@ class FuzzerContext { uint32_t chance_of_constructing_composite_; uint32_t chance_of_copying_object_; uint32_t chance_of_donating_additional_module_; + uint32_t chance_of_making_donor_livesafe_; uint32_t chance_of_merging_blocks_; uint32_t chance_of_moving_block_down_; uint32_t chance_of_obfuscating_constant_; @@ -141,6 +148,7 @@ class FuzzerContext { // Keep them in alphabetical order. uint32_t max_loop_control_partial_count_; uint32_t max_loop_control_peel_count_; + uint32_t max_loop_limit_; // Functions to determine with what probability to go deeper when generating // or mutating constructs recursively. diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 1da53f4262..9d891a59d7 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -15,6 +15,11 @@ #include "source/fuzz/fuzzer_pass.h" #include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_pointer.h" namespace spvtools { namespace fuzz { @@ -128,5 +133,73 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction( } } +uint32_t FuzzerPass::FindOrCreateBoolType() { + opt::analysis::Bool bool_type; + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeBoolean(result)); + return result; +} + +uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) { + opt::analysis::Integer int_type(32, is_signed); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeInt(result, 32, is_signed)); + return result; +} + +uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType( + bool is_signed, SpvStorageClass storage_class) { + auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed); + opt::analysis::Pointer pointer_type( + GetIRContext()->get_type_mgr()->GetType(uint32_type_id), storage_class); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&pointer_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypePointer(result, storage_class, uint32_type_id)); + return result; +} + +uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word, + bool is_signed) { + auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed); + opt::analysis::IntConstant int_constant( + GetIRContext()->get_type_mgr()->GetType(uint32_type_id)->AsInteger(), + {word}); + auto existing_constant = + GetIRContext()->get_constant_mgr()->FindConstant(&int_constant); + if (existing_constant) { + return GetIRContext() + ->get_constant_mgr() + ->GetDefiningInstruction(existing_constant) + ->result_id(); + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddConstantScalar(result, uint32_type_id, {word})); + return result; +} + +uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { + for (auto& inst : GetIRContext()->types_values()) { + if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) { + return inst.result_id(); + } + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddGlobalUndef(result, type_id)); + return result; +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index e3bf8e8d79..09f831f4b9 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -89,7 +89,7 @@ class FuzzerPass { const protobufs::InstructionDescriptor& instruction_descriptor)> maybe_apply_transformation); - // A generic helper for applying a transforamtion that should be appplicable + // A generic helper for applying a transformation that should be applicable // by construction, and adding it to the sequence of applied transformations. template void ApplyTransformation(const TransformationType& transformation) { @@ -99,6 +99,33 @@ class FuzzerPass { *GetTransformations()->add_transformation() = transformation.ToMessage(); } + // Returns the id of an OpTypeBool instruction. If such an instruction does + // not exist, a transformation is applied to add it. + uint32_t FindOrCreateBoolType(); + + // Returns the id of an OpTypeInt instruction, with width 32 and signedness + // specified by |is_signed|. If such an instruction does not exist, a + // transformation is applied to add it. + uint32_t FindOrCreate32BitIntegerType(bool is_signed); + + // Returns the id of an OpTypePointer instruction, with a 32-bit integer base + // type of signedness specified by |is_signed|. If the pointer type or + // required integer base type do not exist, transformations are applied to add + // them. + uint32_t FindOrCreatePointerTo32BitIntegerType(bool is_signed, + SpvStorageClass storage_class); + + // Returns the id of an OpConstant instruction, with 32-bit integer type of + // signedness specified by |is_signed|, with |word| as its value. If either + // the required integer type or the constant do not exist, transformations are + // applied to add them. + uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed); + + // Returns the result id of an instruction of the form: + // %id = OpUndef %|type_id| + // If no such instruction exists, a transformation is applied to add it. + uint32_t FindOrCreateGlobalUndef(uint32_t type_id); + private: opt::IRContext* ir_context_; FactManager* fact_manager_; diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 0587a5069b..b0b9d27ff8 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -62,13 +62,20 @@ void FuzzerPassDonateModules::Apply() { GetFuzzerContext()->RandomIndex(donor_suppliers_))(); assert(donor_ir_context != nullptr && "Supplying of donor failed"); // Donate the supplied module. - DonateSingleModule(donor_ir_context.get()); + // + // Randomly decide whether to make the module livesafe (see + // FactFunctionIsLivesafe); doing so allows it to be used for live code + // injection but restricts its behaviour to allow this, and means that its + // functions cannot be transformed as if they were arbitrary dead code. + bool make_livesafe = GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->ChanceOfMakingDonorLivesafe()); + DonateSingleModule(donor_ir_context.get(), make_livesafe); } while (GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfDonatingAdditionalModule())); } void FuzzerPassDonateModules::DonateSingleModule( - opt::IRContext* donor_ir_context) { + opt::IRContext* donor_ir_context, bool make_livesafe) { // The ids used by the donor module may very well clash with ids defined in // the recipient module. Furthermore, some instructions defined in the donor // module will be equivalent to instructions defined in the recipient module, @@ -91,7 +98,7 @@ void FuzzerPassDonateModules::DonateSingleModule( HandleExternalInstructionImports(donor_ir_context, &original_id_to_donated_id); HandleTypesAndValues(donor_ir_context, &original_id_to_donated_id); - HandleFunctions(donor_ir_context, &original_id_to_donated_id); + HandleFunctions(donor_ir_context, &original_id_to_donated_id, make_livesafe); // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3115) Handle some // kinds of decoration. @@ -420,7 +427,8 @@ void FuzzerPassDonateModules::HandleTypesAndValues( void FuzzerPassDonateModules::HandleFunctions( opt::IRContext* donor_ir_context, - std::map* original_id_to_donated_id) { + std::map* original_id_to_donated_id, + bool make_livesafe) { // Get the ids of functions in the donor module, topologically sorted // according to the donor's call graph. auto topological_order = @@ -506,7 +514,146 @@ void FuzzerPassDonateModules::HandleFunctions( : 0, input_operands)); }); - ApplyTransformation(TransformationAddFunction(donated_instructions)); + + if (make_livesafe) { + // Various types and constants must be in place for a function to be made + // live-safe. Add them if not already present. + FindOrCreateBoolType(); // Needed for comparisons + FindOrCreatePointerTo32BitIntegerType( + false, SpvStorageClassFunction); // Needed for adding loop limiters + FindOrCreate32BitIntegerConstant( + 0, false); // Needed for initializing loop limiters + FindOrCreate32BitIntegerConstant( + 1, false); // Needed for incrementing loop limiters + + // Get a fresh id for the variable that will be used as a loop limiter. + const uint32_t loop_limiter_variable_id = + GetFuzzerContext()->GetFreshId(); + // Choose a random loop limit, and add the required constant to the + // module if not already there. + const uint32_t loop_limit = FindOrCreate32BitIntegerConstant( + GetFuzzerContext()->GetRandomLoopLimit(), false); + + // Consider every loop header in the function to donate, and create a + // structure capturing the ids to be used for manipulating the loop + // limiter each time the loop is iterated. + std::vector loop_limiters; + for (auto& block : *function_to_donate) { + if (block.IsLoopHeader()) { + protobufs::LoopLimiterInfo loop_limiter; + // Grab the loop header's id, mapped to its donated value. + loop_limiter.set_loop_header_id( + original_id_to_donated_id->at(block.id())); + // Get fresh ids that will be used to load the loop limiter, increment + // it, compare it with the loop limit, and an id for a new block that + // will contain the loop's original terminator. + loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId()); + loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId()); + loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId()); + loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId()); + loop_limiters.emplace_back(loop_limiter); + } + } + + // Consider every access chain in the function to donate, and create a + // structure containing the ids necessary to clamp the access chain + // indices to be in-bounds. + std::vector + access_chain_clamping_info; + for (auto& block : *function_to_donate) { + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: { + protobufs::AccessChainClampingInfo clamping_info; + clamping_info.set_access_chain_id( + original_id_to_donated_id->at(inst.result_id())); + + auto base_object = donor_ir_context->get_def_use_mgr()->GetDef( + inst.GetSingleWordInOperand(0)); + assert(base_object && "The base object must exist."); + auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef( + base_object->type_id()); + assert(pointer_type && + pointer_type->opcode() == SpvOpTypePointer && + "The base object must have pointer type."); + + auto should_be_composite_type = + donor_ir_context->get_def_use_mgr()->GetDef( + pointer_type->GetSingleWordInOperand(1)); + + // Walk the access chain, creating fresh ids to facilitate + // clamping each index. For simplicity we do this for every + // index, even though constant indices will not end up being + // clamped. + for (uint32_t index = 1; index < inst.NumInOperands(); index++) { + auto compare_and_select_ids = + clamping_info.add_compare_and_select_ids(); + compare_and_select_ids->set_first( + GetFuzzerContext()->GetFreshId()); + compare_and_select_ids->set_second( + GetFuzzerContext()->GetFreshId()); + + // Get the bound for the component being indexed into. + uint32_t bound = + TransformationAddFunction::GetBoundForCompositeIndex( + donor_ir_context, *should_be_composite_type); + const uint32_t index_id = inst.GetSingleWordInOperand(index); + auto index_inst = + donor_ir_context->get_def_use_mgr()->GetDef(index_id); + auto index_type_inst = + donor_ir_context->get_def_use_mgr()->GetDef( + index_inst->type_id()); + assert(index_type_inst->opcode() == SpvOpTypeInt); + assert(index_type_inst->GetSingleWordInOperand(0) == 32); + opt::analysis::Integer* index_int_type = + donor_ir_context->get_type_mgr() + ->GetType(index_type_inst->result_id()) + ->AsInteger(); + if (index_inst->opcode() != SpvOpConstant) { + // We will have to clamp this index, so we need a constant + // whose value is one less than the bound, to compare + // against and to use as the clamped value. + FindOrCreate32BitIntegerConstant(bound - 1, + index_int_type->IsSigned()); + } + should_be_composite_type = + TransformationAddFunction::FollowCompositeIndex( + donor_ir_context, *should_be_composite_type, index_id); + } + access_chain_clamping_info.push_back(clamping_info); + break; + } + default: + break; + } + } + } + + // If the function contains OpKill or OpUnreachable instructions, and has + // non-void return type, then we need a value %v to use in order to turn + // these into instructions of the form OpReturn %v. + uint32_t kill_unreachable_return_value_id; + auto function_return_type_inst = + donor_ir_context->get_def_use_mgr()->GetDef( + function_to_donate->type_id()); + if (function_return_type_inst->opcode() == SpvOpTypeVoid) { + // The return type is void, so we don't need a return value. + kill_unreachable_return_value_id = 0; + } else { + // We do need a return value; we use OpUndef. + kill_unreachable_return_value_id = + FindOrCreateGlobalUndef(function_return_type_inst->type_id()); + } + // Add the function in a livesafe manner. + ApplyTransformation(TransformationAddFunction( + donated_instructions, loop_limiter_variable_id, loop_limit, + loop_limiters, kill_unreachable_return_value_id, + access_chain_clamping_info)); + } else { + // Add the function in a non-livesafe manner. + ApplyTransformation(TransformationAddFunction(donated_instructions)); + } } } diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index d719e878eb..ef529db707 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -38,8 +38,10 @@ class FuzzerPassDonateModules : public FuzzerPass { void Apply() override; // Donates the global declarations and functions of |donor_ir_context| into - // the fuzzer pass's IR context. - void DonateSingleModule(opt::IRContext* donor_ir_context); + // the fuzzer pass's IR context. |make_livesafe| dictates whether the + // functions of the donated module will be made livesafe (see + // FactFunctionIsLivesafe). + void DonateSingleModule(opt::IRContext* donor_ir_context, bool make_livesafe); private: // Adapts a storage class coming from a donor module so that it will work @@ -68,9 +70,12 @@ class FuzzerPassDonateModules : public FuzzerPass { // functions in |donor_ir_context|'s call graph in a reverse-topologically- // sorted order (leaves-to-root), adding each function to the recipient // module, rewritten to use fresh ids and using |original_id_to_donated_id| to - // remap ids. + // remap ids. The |make_livesafe| argument captures whether the functions in + // the module are required to be made livesafe before being added to the + // recipient. void HandleFunctions(opt::IRContext* donor_ir_context, - std::map* original_id_to_donated_id); + std::map* original_id_to_donated_id, + bool make_livesafe); // Returns the ids of all functions in |context| in a topological order in // relation to the call graph of |context|, which is assumed to be recursion- diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 085246e7c9..b2ace38f17 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -103,10 +103,10 @@ bool PhiIdsOkForNewEdge( } phi_index++; } - // Return false if not all of the ids for extending OpPhi instructions are - // needed. This might turn out to be stricter than necessary; perhaps it would - // be OK just to not use the ids in this case. - return phi_index == static_cast(phi_ids.size()); + // We allow some of the ids provided for extending OpPhi instructions to be + // unused. Their presence does no harm, and requiring a perfect match may + // make transformations less likely to cleanly apply. + return true; } uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) { @@ -158,13 +158,11 @@ void AddUnreachableEdgeAndUpdateOpPhis( break; } assert(phi_index < static_cast(phi_ids.size()) && - "There should be exactly one phi id per OpPhi instruction."); + "There should be at least one phi id per OpPhi instruction."); inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}}); inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}}); phi_index++; } - assert(phi_index == static_cast(phi_ids.size()) && - "There should be exactly one phi id per OpPhi instruction."); } } diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 08f39867aa..2f37a7d967 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -167,6 +167,7 @@ message Fact { FactConstantUniform constant_uniform_fact = 1; FactDataSynonym data_synonym_fact = 2; FactBlockIsDead block_is_dead_fact = 3; + FactFunctionIsLivesafe function_is_livesafe_fact = 4; } } @@ -210,6 +211,77 @@ message FactBlockIsDead { uint32 block_id = 1; } +message FactFunctionIsLivesafe { + + // Records the fact that a function is guaranteed to be "livesafe", meaning + // that it will not make out-of-bounds accesses, does not contain reachable + // OpKill or OpUnreachable instructions, does not contain loops that will + // execute for large numbers of iterations, and only invokes other livesafe + // functions. + + uint32 function_id = 1; +} + +message AccessChainClampingInfo { + + // When making a function livesafe it is necessary to clamp the indices that + // occur as operands to access chain instructions so that they are guaranteed + // to be in bounds. This message type allows an access chain instruction to + // have an associated sequence of ids that are reserved for comparing an + // access chain index with a bound (e.g. an array size), and selecting + // between the access chain index (if it is within bounds) and the bound (if + // it is not). + // + // This allows turning an instruction of the form: + // + // %result = OpAccessChain %type %object ... %index ... + // + // into: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + // %result = OpAccessChain %type %object ... %t2 ... + + // The result id of an OpAccessChain or OpInBoundsAccessChain instruction. + uint32 access_chain_id = 1; + + // A series of pairs of fresh ids, one per access chain index, for the results + // of a compare instruction and a select instruction, serving the roles of %t1 + // and %t2 in the above example. + repeated UInt32Pair compare_and_select_ids = 2; + +} + +message LoopLimiterInfo { + + // Structure capturing the information required to manipulate a loop limiter + // at a loop header. + + // The header for the loop. + uint32 loop_header_id = 1; + + // A fresh id into which the loop limiter's current value can be loaded. + uint32 load_id = 2; + + // A fresh id that can be used to increment the loaded value by 1. + uint32 increment_id = 3; + + // A fresh id that can be used to compare the loaded value with the loop + // limit. + uint32 compare_id = 4; + + // A fresh id that can be used to compute the conjunction or disjunction of + // an original loop exit condition with |compare_id|, if the loop's back edge + // block can conditionally exit the loop. + uint32 logical_op_id = 5; + + // A sequence of ids suitable for extending OpPhi instructions of the loop + // merge block if it did not previously have an incoming edge from the loop + // back edge block. + repeated uint32 phi_id = 6; + +} + message TransformationSequence { repeated Transformation transformation = 1; } @@ -366,6 +438,33 @@ message TransformationAddFunction { // The series of instructions that comprise the function. repeated Instruction instruction = 1; + // True if and only if the given function should be made livesafe (see + // FactFunctionIsLivesafe for definition). + bool is_livesafe = 2; + + // Fresh id for a new variable that will serve as a "loop limiter" for the + // function; only relevant if |is_livesafe| holds. + uint32 loop_limiter_variable_id = 3; + + // Id of an existing unsigned integer constant providing the maximum value + // that the loop limiter can reach before the loop is broken from; only + // relevant if |is_livesafe| holds. + uint32 loop_limit_constant_id = 4; + + // Fresh ids for each loop in the function that allow the loop limiter to be + // manipulated; only relevant if |is_livesafe| holds. + repeated LoopLimiterInfo loop_limiter_info = 5; + + // Id of an existing global value with the same return type as the function + // that can be used to replace OpKill and OpReachable instructions with + // ReturnValue instructions. Ignored if the function has void return type. + uint32 kill_unreachable_return_value_id = 6; + + // A mapping (represented as a sequence) from every access chain result id in + // the function to the ids required to clamp its indices to ensure they are in + // bounds. + repeated AccessChainClampingInfo access_chain_clamping_info = 7; + } message TransformationAddGlobalUndef { diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp index 5e53961bf1..8b8b2ddd50 100644 --- a/source/fuzz/transformation_add_function.cpp +++ b/source/fuzz/transformation_add_function.cpp @@ -29,11 +29,95 @@ TransformationAddFunction::TransformationAddFunction( for (auto& instruction : instructions) { *message_.add_instruction() = instruction; } + message_.set_is_livesafe(false); +} + +TransformationAddFunction::TransformationAddFunction( + const std::vector& instructions, + uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id, + const std::vector& loop_limiters, + uint32_t kill_unreachable_return_value_id, + const std::vector& + access_chain_clampers) { + for (auto& instruction : instructions) { + *message_.add_instruction() = instruction; + } + message_.set_is_livesafe(true); + message_.set_loop_limiter_variable_id(loop_limiter_variable_id); + message_.set_loop_limit_constant_id(loop_limit_constant_id); + for (auto& loop_limiter : loop_limiters) { + *message_.add_loop_limiter_info() = loop_limiter; + } + message_.set_kill_unreachable_return_value_id( + kill_unreachable_return_value_id); + for (auto& access_clamper : access_chain_clampers) { + *message_.add_access_chain_clamping_info() = access_clamper; + } } bool TransformationAddFunction::IsApplicable( opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + const spvtools::fuzz::FactManager& fact_manager) const { + // This transformation may use a lot of ids, all of which need to be fresh + // and distinct. This set tracks them. + std::set ids_used_by_this_transformation; + + // Ensure that all result ids in the new function are fresh and distinct. + for (auto& instruction : message_.instruction()) { + if (instruction.result_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + instruction.result_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + } + } + + if (message_.is_livesafe()) { + // Ensure that all ids provided for making the function livesafe are fresh + // and distinct. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.loop_limiter_variable_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + for (auto& loop_limiter_info : message_.loop_limiter_info()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.load_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.increment_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.compare_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.logical_op_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + } + for (auto& access_chain_clamping_info : + message_.access_chain_clamping_info()) { + for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.first(), context, &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), context, &ids_used_by_this_transformation)) { + return false; + } + } + } + } + // Because checking all the conditions for a function to be valid is a big // job that the SPIR-V validator can already do, a "try it and see" approach // is taken here. @@ -47,18 +131,49 @@ bool TransformationAddFunction::IsApplicable( if (!TryToAddFunction(cloned_module.get())) { return false; } - // Having managed to add the new function to the cloned module, we ascertain - // whether the cloned module is still valid. If it is, the transformation is - // applicable. + + if (message_.is_livesafe()) { + // We make the cloned module livesafe. + if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) { + return false; + } + } + + // Having managed to add the new function to the cloned module, and + // potentially also made it livesafe, we ascertain whether the cloned module + // is still valid. If it is, the transformation is applicable. return fuzzerutil::IsValid(cloned_module.get()); } void TransformationAddFunction::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { - auto success = TryToAddFunction(context); + opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + // Add the function to the module. As the transformation is applicable, this + // should succeed. + bool success = TryToAddFunction(context); assert(success && "The function should be successfully added."); (void)(success); // Keep release builds happy (otherwise they may complain // that |success| is not used). + + if (message_.is_livesafe()) { + // Make the function livesafe, which also should succeed. + success = TryToMakeFunctionLivesafe(context, *fact_manager); + assert(success && "It should be possible to make the function livesafe."); + (void)(success); // Keep release builds happy. + + // Inform the fact manager that the function is livesafe. + assert(message_.instruction(0).opcode() == SpvOpFunction && + "The first instruction of an 'add function' transformation must be " + "OpFunction."); + fact_manager->AddFactFunctionIsLivesafe( + message_.instruction(0).result_id()); + } else { + // Inform the fact manager that all blocks in the function are dead. + for (auto& inst : message_.instruction()) { + if (inst.opcode() == SpvOpLabel) { + fact_manager->AddFactBlockIsDead(inst.result_id()); + } + } + } context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } @@ -149,8 +264,634 @@ bool TransformationAddFunction::TryToAddFunction( new_function->SetFunctionEnd( InstructionFromMessage(context, message_.instruction(instruction_index))); context->AddFunction(std::move(new_function)); + + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + return true; } +bool TransformationAddFunction::TryToMakeFunctionLivesafe( + opt::IRContext* context, const FactManager& fact_manager) const { + assert(message_.is_livesafe() && "Precondition: is_livesafe must hold."); + + // Get a pointer to the added function. + opt::Function* added_function = nullptr; + for (auto& function : *context->module()) { + if (function.result_id() == message_.instruction(0).result_id()) { + added_function = &function; + break; + } + } + assert(added_function && "The added function should have been found."); + + if (!TryToAddLoopLimiters(context, added_function)) { + // Adding loop limiters did not work; bail out. + return false; + } + + // Consider all the instructions in the function, and: + // - attempt to replace OpKill and OpUnreachable with return instructions + // - attempt to clamp access chains to be within bounds + // - check that OpFunctionCall instructions are only to livesafe functions + for (auto& block : *added_function) { + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpKill: + case SpvOpUnreachable: + if (!TryToTurnKillOrUnreachableIntoReturn(context, added_function, + &inst)) { + return false; + } + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (!TryToClampAccessChainIndices(context, &inst)) { + return false; + } + break; + case SpvOpFunctionCall: + // A livesafe function my only call other livesafe functions. + if (!fact_manager.FunctionIsLivesafe( + inst.GetSingleWordInOperand(0))) { + return false; + } + default: + break; + } + } + } + return true; +} + +bool TransformationAddFunction::TryToAddLoopLimiters( + opt::IRContext* context, opt::Function* added_function) const { + // Collect up all the loop headers so that we can subsequently add loop + // limiting logic. + std::vector loop_headers; + for (auto& block : *added_function) { + if (block.IsLoopHeader()) { + loop_headers.push_back(&block); + } + } + + if (loop_headers.empty()) { + // There are no loops, so no need to add any loop limiters. + return true; + } + + // Check that the module contains appropriate ingredients for declaring and + // manipulating a loop limiter. + + auto loop_limit_constant_id_instr = + context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id()); + if (!loop_limit_constant_id_instr || + loop_limit_constant_id_instr->opcode() != SpvOpConstant) { + // The loop limit constant id instruction must exist and have an + // appropriate opcode. + return false; + } + + auto loop_limit_type = context->get_def_use_mgr()->GetDef( + loop_limit_constant_id_instr->type_id()); + if (loop_limit_type->opcode() != SpvOpTypeInt || + loop_limit_type->GetSingleWordInOperand(0) != 32) { + // The type of the loop limit constant must be 32-bit integer. It + // doesn't actually matter whether the integer is signed or not. + return false; + } + + // Find the id of the "unsigned int" type. + opt::analysis::Integer unsigned_int_type(32, false); + uint32_t unsigned_int_type_id = + context->get_type_mgr()->GetId(&unsigned_int_type); + if (!unsigned_int_type_id) { + // Unsigned int is not available; we need this type in order to add loop + // limiters. + return false; + } + auto registered_unsigned_int_type = + context->get_type_mgr()->GetRegisteredType(&unsigned_int_type); + + // Look for 0 of type unsigned int. + opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(), + {0}); + auto registered_zero = context->get_constant_mgr()->FindConstant(&zero); + if (!registered_zero) { + // We need 0 in order to be able to initialize loop limiters. + return false; + } + uint32_t zero_id = context->get_constant_mgr() + ->GetDefiningInstruction(registered_zero) + ->result_id(); + + // Look for 1 of type unsigned int. + opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(), + {1}); + auto registered_one = context->get_constant_mgr()->FindConstant(&one); + if (!registered_one) { + // We need 1 in order to be able to increment loop limiters. + return false; + } + uint32_t one_id = context->get_constant_mgr() + ->GetDefiningInstruction(registered_one) + ->result_id(); + + // Look for pointer-to-unsigned int type. + opt::analysis::Pointer pointer_to_unsigned_int_type( + registered_unsigned_int_type, SpvStorageClassFunction); + uint32_t pointer_to_unsigned_int_type_id = + context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type); + if (!pointer_to_unsigned_int_type_id) { + // We need pointer-to-unsigned int in order to declare the loop limiter + // variable. + return false; + } + + // Look for bool type. + opt::analysis::Bool bool_type; + uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type); + if (!bool_type_id) { + // We need bool in order to compare the loop limiter's value with the loop + // limit constant. + return false; + } + + // Declare the loop limiter variable at the start of the function's entry + // block, via an instruction of the form: + // %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero + added_function->begin()->begin()->InsertBefore(MakeUnique( + context, SpvOpVariable, pointer_to_unsigned_int_type_id, + message_.loop_limiter_variable_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, + {SPV_OPERAND_TYPE_ID, {zero_id}}}))); + // Update the module's id bound since we have added the loop limiter + // variable id. + fuzzerutil::UpdateModuleIdBound(context, message_.loop_limiter_variable_id()); + + // Consider each loop in turn. + for (auto loop_header : loop_headers) { + // Look for the loop's back-edge block. This is a predecessor of the loop + // header that is dominated by the loop header. + uint32_t back_edge_block_id = 0; + for (auto pred : context->cfg()->preds(loop_header->id())) { + if (context->GetDominatorAnalysis(added_function) + ->Dominates(loop_header->id(), pred)) { + back_edge_block_id = pred; + break; + } + } + if (!back_edge_block_id) { + // The loop's back-edge block must be unreachable. This means that the + // loop cannot iterate, so there is no need to make it lifesafe; we can + // move on from this loop. + continue; + } + auto back_edge_block = context->cfg()->block(back_edge_block_id); + + // Go through the sequence of loop limiter infos and find the one + // corresponding to this loop. + bool found = false; + protobufs::LoopLimiterInfo loop_limiter_info; + for (auto& info : message_.loop_limiter_info()) { + if (info.loop_header_id() == loop_header->id()) { + loop_limiter_info = info; + found = true; + break; + } + } + if (!found) { + // We don't have loop limiter info for this loop header. + return false; + } + + // The back-edge block either has the form: + // + // (1) + // + // %l = OpLabel + // ... instructions ... + // OpBranch %loop_header + // + // (2) + // + // %l = OpLabel + // ... instructions ... + // OpBranchConditional %c %loop_header %loop_merge + // + // (3) + // + // %l = OpLabel + // ... instructions ... + // OpBranchConditional %c %loop_merge %loop_header + // + // We turn these into the following: + // + // (1) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // OpBranchConditional %t3 %loop_merge %loop_header + // + // (2) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpULessThan %bool %t1 %loop_limit + // %t4 = OpLogicalAnd %bool %c %t3 + // OpBranchConditional %t4 %loop_header %loop_merge + // + // (3) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // %t4 = OpLogicalOr %bool %c %t3 + // OpBranchConditional %t4 %loop_merge %loop_header + + auto back_edge_block_terminator = back_edge_block->terminator(); + bool compare_using_greater_than_equal; + if (back_edge_block_terminator->opcode() == SpvOpBranch) { + compare_using_greater_than_equal = true; + } else { + assert(back_edge_block_terminator->opcode() == SpvOpBranchConditional); + assert(((back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->id() && + back_edge_block_terminator->GetSingleWordInOperand(2) == + loop_header->MergeBlockId()) || + (back_edge_block_terminator->GetSingleWordInOperand(2) == + loop_header->id() && + back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->MergeBlockId())) && + "A back edge edge block must branch to" + " either the loop header or merge"); + compare_using_greater_than_equal = + back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->MergeBlockId(); + } + + std::vector> new_instructions; + + // Add a load from the loop limiter variable, of the form: + // %t1 = OpLoad %uint32 %loop_limiter + new_instructions.push_back(MakeUnique( + context, SpvOpLoad, unsigned_int_type_id, loop_limiter_info.load_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}}))); + + // Increment the loaded value: + // %t2 = OpIAdd %uint32 %t1 %one + new_instructions.push_back(MakeUnique( + context, SpvOpIAdd, unsigned_int_type_id, + loop_limiter_info.increment_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}}, + {SPV_OPERAND_TYPE_ID, {one_id}}}))); + + // Store the incremented value back to the loop limiter variable: + // OpStore %loop_limiter %t2 + new_instructions.push_back(MakeUnique( + context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}, + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}}))); + + // Compare the loaded value with the loop limit; either: + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // or + // %t3 = OpULessThan %bool %t1 %loop_limit + new_instructions.push_back(MakeUnique( + context, + compare_using_greater_than_equal ? SpvOpUGreaterThanEqual + : SpvOpULessThan, + bool_type_id, loop_limiter_info.compare_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.loop_limit_constant_id()}}}))); + + if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) { + new_instructions.push_back(MakeUnique( + context, + compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd, + bool_type_id, loop_limiter_info.logical_op_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, + {back_edge_block_terminator->GetSingleWordInOperand(0)}}, + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}}))); + } + + // Add the new instructions at the end of the back edge block, before the + // terminator and any loop merge instruction (as the back edge block can + // be the loop header). + if (back_edge_block->GetLoopMergeInst()) { + back_edge_block->GetLoopMergeInst()->InsertBefore( + std::move(new_instructions)); + } else { + back_edge_block_terminator->InsertBefore(std::move(new_instructions)); + } + + if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) { + back_edge_block_terminator->SetInOperand( + 0, {loop_limiter_info.logical_op_id()}); + } else { + assert(back_edge_block_terminator->opcode() == SpvOpBranch && + "Back-edge terminator must be OpBranch or OpBranchConditional"); + + // Check that, if the merge block starts with OpPhi instructions, suitable + // ids have been provided to give these instructions a value corresponding + // to the new incoming edge from the back edge block. + auto merge_block = context->cfg()->block(loop_header->MergeBlockId()); + if (!fuzzerutil::PhiIdsOkForNewEdge(context, back_edge_block, merge_block, + loop_limiter_info.phi_id())) { + return false; + } + + // Augment OpPhi instructions at the loop merge with the given ids. + uint32_t phi_index = 0; + for (auto& inst : *merge_block) { + if (inst.opcode() != SpvOpPhi) { + break; + } + assert(phi_index < + static_cast(loop_limiter_info.phi_id().size()) && + "There should be at least one phi id per OpPhi instruction."); + inst.AddOperand( + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.phi_id(phi_index)}}); + inst.AddOperand({SPV_OPERAND_TYPE_ID, {back_edge_block_id}}); + phi_index++; + } + + // Add the new edge, by changing OpBranch to OpBranchConditional. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3162): This + // could be a problem if the merge block was originally unreachable: it + // might now be dominated by other blocks that it appears earlier than in + // the module. + back_edge_block_terminator->SetOpcode(SpvOpBranchConditional); + back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}, + {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()} + + }, + {SPV_OPERAND_TYPE_ID, {loop_header->id()}}})); + } + + // Update the module's id bound with respect to the various ids that + // have been used for loop limiter manipulation. + fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.load_id()); + fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.increment_id()); + fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.compare_id()); + fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.logical_op_id()); + } + return true; +} + +bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn( + opt::IRContext* context, opt::Function* added_function, + opt::Instruction* kill_or_unreachable_inst) const { + assert((kill_or_unreachable_inst->opcode() == SpvOpKill || + kill_or_unreachable_inst->opcode() == SpvOpUnreachable) && + "Precondition: instruction must be OpKill or OpUnreachable."); + + // Get the function's return type. + auto function_return_type_inst = + context->get_def_use_mgr()->GetDef(added_function->type_id()); + + if (function_return_type_inst->opcode() == SpvOpTypeVoid) { + // The function has void return type, so change this instruction to + // OpReturn. + kill_or_unreachable_inst->SetOpcode(SpvOpReturn); + } else { + // The function has non-void return type, so change this instruction + // to OpReturnValue, using the value id provided with the + // transformation. + + // We first check that the id, %id, provided with the transformation + // specifically to turn OpKill and OpUnreachable instructions into + // OpReturnValue %id has the same type as the function's return type. + if (context->get_def_use_mgr() + ->GetDef(message_.kill_unreachable_return_value_id()) + ->type_id() != function_return_type_inst->result_id()) { + return false; + } + kill_or_unreachable_inst->SetOpcode(SpvOpReturnValue); + kill_or_unreachable_inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {message_.kill_unreachable_return_value_id()}}}); + } + return true; +} + +bool TransformationAddFunction::TryToClampAccessChainIndices( + opt::IRContext* context, opt::Instruction* access_chain_inst) const { + assert((access_chain_inst->opcode() == SpvOpAccessChain || + access_chain_inst->opcode() == SpvOpInBoundsAccessChain) && + "Precondition: instruction must be OpAccessChain or " + "OpInBoundsAccessChain."); + + // Find the AccessChainClampingInfo associated with this access chain. + const protobufs::AccessChainClampingInfo* access_chain_clamping_info = + nullptr; + for (auto& clamping_info : message_.access_chain_clamping_info()) { + if (clamping_info.access_chain_id() == access_chain_inst->result_id()) { + access_chain_clamping_info = &clamping_info; + break; + } + } + if (!access_chain_clamping_info) { + // No access chain clamping information was found; the function cannot be + // made livesafe. + return false; + } + + // Check that there is a (compare_id, select_id) pair for every + // index associated with the instruction. + if (static_cast( + access_chain_clamping_info->compare_and_select_ids().size()) != + access_chain_inst->NumInOperands() - 1) { + return false; + } + + // Walk the access chain, clamping each index to be within bounds if it is + // not a constant. + auto base_object = context->get_def_use_mgr()->GetDef( + access_chain_inst->GetSingleWordInOperand(0)); + assert(base_object && "The base object must exist."); + auto pointer_type = + context->get_def_use_mgr()->GetDef(base_object->type_id()); + assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer && + "The base object must have pointer type."); + auto should_be_composite_type = context->get_def_use_mgr()->GetDef( + pointer_type->GetSingleWordInOperand(1)); + + // Consider each index input operand in turn (operand 0 is the base object). + for (uint32_t index = 1; index < access_chain_inst->NumInOperands(); + index++) { + // We are going to turn: + // + // %result = OpAccessChain %type %object ... %index ... + // + // into: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + // %result = OpAccessChain %type %object ... %t2 ... + // + // ... unless %index is already a constant. + + // Get the bound for the composite being indexed into; e.g. the number of + // columns of matrix or the size of an array. + uint32_t bound = + GetBoundForCompositeIndex(context, *should_be_composite_type); + + // Get the instruction associated with the index and figure out its integer + // type. + const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index); + auto index_inst = context->get_def_use_mgr()->GetDef(index_id); + auto index_type_inst = + context->get_def_use_mgr()->GetDef(index_inst->type_id()); + assert(index_type_inst->opcode() == SpvOpTypeInt); + assert(index_type_inst->GetSingleWordInOperand(0) == 32); + opt::analysis::Integer* index_int_type = + context->get_type_mgr() + ->GetType(index_type_inst->result_id()) + ->AsInteger(); + + if (index_inst->opcode() != SpvOpConstant) { + // The index is non-constant so we need to clamp it. + assert(should_be_composite_type->opcode() != SpvOpTypeStruct && + "Access chain indices into structures are required to be " + "constants."); + opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1}); + if (!context->get_constant_mgr()->FindConstant(&bound_minus_one)) { + // We do not have an integer constant whose value is |bound| -1. + return false; + } + + opt::analysis::Bool bool_type; + uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type); + if (!bool_type_id) { + // Bool type is not declared; we cannot do a comparison. + return false; + } + + uint32_t bound_minus_one_id = + context->get_constant_mgr() + ->GetDefiningInstruction(&bound_minus_one) + ->result_id(); + + uint32_t compare_id = + access_chain_clamping_info->compare_and_select_ids(index - 1).first(); + uint32_t select_id = + access_chain_clamping_info->compare_and_select_ids(index - 1) + .second(); + std::vector> new_instructions; + + // Compare the index with the bound via an instruction of the form: + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + new_instructions.push_back(MakeUnique( + context, SpvOpULessThanEqual, bool_type_id, compare_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + // Select the index if in-bounds, otherwise one less than the bound: + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + new_instructions.push_back(MakeUnique( + context, SpvOpSelect, index_type_inst->result_id(), select_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {compare_id}}, + {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + // Add the new instructions before the access chain + access_chain_inst->InsertBefore(std::move(new_instructions)); + + // Replace %index with %t2. + access_chain_inst->SetInOperand(index, {select_id}); + fuzzerutil::UpdateModuleIdBound(context, compare_id); + fuzzerutil::UpdateModuleIdBound(context, select_id); + } else { + // TODO(afd): At present the SPIR-V spec is not clear on whether + // statically out-of-bounds indices mean that a module is invalid (so + // that it should be rejected by the validator), or that such accesses + // yield undefined results. Via the following assertion, we assume that + // functions added to the module do not feature statically out-of-bounds + // accesses. + // Assert that the index is smaller (unsigned) than this value. + // Return false if it is not (to keep compilers happy). + if (index_inst->GetSingleWordInOperand(0) >= bound) { + assert(false && + "The function has a statically out-of-bounds access; " + "this should not occur."); + return false; + } + } + should_be_composite_type = + FollowCompositeIndex(context, *should_be_composite_type, index_id); + } + return true; +} + +uint32_t TransformationAddFunction::GetBoundForCompositeIndex( + opt::IRContext* context, const opt::Instruction& composite_type_inst) { + switch (composite_type_inst.opcode()) { + case SpvOpTypeArray: + return fuzzerutil::GetArraySize(composite_type_inst, context); + case SpvOpTypeMatrix: + case SpvOpTypeVector: + return composite_type_inst.GetSingleWordInOperand(1); + case SpvOpTypeStruct: { + return fuzzerutil::GetNumberOfStructMembers(composite_type_inst); + } + default: + assert(false && "Unknown composite type."); + return 0; + } +} + +opt::Instruction* TransformationAddFunction::FollowCompositeIndex( + opt::IRContext* context, const opt::Instruction& composite_type_inst, + uint32_t index_id) { + uint32_t sub_object_type_id; + switch (composite_type_inst.opcode()) { + case SpvOpTypeArray: + sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); + break; + case SpvOpTypeMatrix: + case SpvOpTypeVector: + sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: { + auto index_inst = context->get_def_use_mgr()->GetDef(index_id); + assert(index_inst->opcode() == SpvOpConstant); + assert( + context->get_def_use_mgr()->GetDef(index_inst->type_id())->opcode() == + SpvOpTypeInt); + assert(context->get_def_use_mgr() + ->GetDef(index_inst->type_id()) + ->GetSingleWordInOperand(0) == 32); + uint32_t index_value = index_inst->GetSingleWordInOperand(0); + sub_object_type_id = + composite_type_inst.GetSingleWordInOperand(index_value); + break; + } + default: + assert(false && "Unknown composite type."); + sub_object_type_id = 0; + break; + } + assert(sub_object_type_id && "No sub-object found."); + return context->get_def_use_mgr()->GetDef(sub_object_type_id); +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/transformation_add_function.h b/source/fuzz/transformation_add_function.h index fee2732a90..848b799fca 100644 --- a/source/fuzz/transformation_add_function.h +++ b/source/fuzz/transformation_add_function.h @@ -28,26 +28,56 @@ class TransformationAddFunction : public Transformation { explicit TransformationAddFunction( const protobufs::TransformationAddFunction& message); + // Creates a transformation to add a non live-safe function. explicit TransformationAddFunction( const std::vector& instructions); + // Creates a transformation to add a live-safe function. + TransformationAddFunction( + const std::vector& instructions, + uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id, + const std::vector& loop_limiters, + uint32_t kill_unreachable_return_value_id, + const std::vector& + access_chain_clampers); + // - |message_.instruction| must correspond to a sufficiently well-formed // sequence of instructions that a function can be created from them + // - If |message_.is_livesafe| holds then |message_| must contain suitable + // ingredients to make the function livesafe, and the function must only + // invoke other livesafe functions // - Adding the created function to the module must lead to a valid module. bool IsApplicable(opt::IRContext* context, const FactManager& fact_manager) const override; - // Adds the function defined by |message_.instruction| to the module + // Adds the function defined by |message_.instruction| to the module, making + // it livesafe if |message_.is_livesafe| holds. void Apply(opt::IRContext* context, FactManager* fact_manager) const override; protobufs::Transformation ToMessage() const override; + // Helper method that returns the bound for indexing into a composite of type + // |composite_type_inst|, i.e. the number of fields of a struct, the size of + // an array, the number of components of a vector, or the number of columns of + // a matrix. + static uint32_t GetBoundForCompositeIndex( + opt::IRContext* context, const opt::Instruction& composite_type_inst); + + // Helper method that, given composite type |composite_type_inst|, returns the + // type of the sub-object at index |index_id|, which is required to be in- + // bounds. + static opt::Instruction* FollowCompositeIndex( + opt::IRContext* context, const opt::Instruction& composite_type_inst, + uint32_t index_id); + private: // Attempts to create a function from the series of instructions in - // |message_.instruction| and add it to |context|. Returns false if this is - // not possible due to the messages not respecting the basic structure of a - // function, e.g. if there is no OpFunction instruction or no blocks; in this - // case |context| is left in an indeterminate state. + // |message_.instruction| and add it to |context|. + // + // Returns false if adding the function is not possible due to the messages + // not respecting the basic structure of a function, e.g. if there is no + // OpFunction instruction or no blocks; in this case |context| is left in an + // indeterminate state. // // Otherwise returns true. Whether |context| is valid after addition of the // function depends on the contents of |message_.instruction|. @@ -61,6 +91,30 @@ class TransformationAddFunction : public Transformation { // to add the function. bool TryToAddFunction(opt::IRContext* context) const; + // Should only be called if |message_.is_livesafe| holds. Attempts to make + // the function livesafe (see FactFunctionIsLivesafe for a definition). + // Returns false if this is not possible, due to |message_| or |context| not + // containing sufficient ingredients (such as types and fresh ids) to add + // the instrumentation necessary to make the function livesafe. + bool TryToMakeFunctionLivesafe(opt::IRContext* context, + const FactManager& fact_manager) const; + + // A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting + // logic. + bool TryToAddLoopLimiters(opt::IRContext* context, + opt::Function* added_function) const; + + // A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and + // OpUnreachable instructions into return instructions. + bool TryToTurnKillOrUnreachableIntoReturn( + opt::IRContext* context, opt::Function* added_function, + opt::Instruction* kill_or_unreachable_inst) const; + + // A helper for TryToMakeFunctionLivesafe that tries to clamp access chain + // indices so that they are guaranteed to be in-bounds. + bool TryToClampAccessChainIndices(opt::IRContext* context, + opt::Instruction* access_chain_inst) const; + protobufs::TransformationAddFunction message_; }; diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 1b308c4daf..c097e6cfe6 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -254,11 +254,21 @@ bool TransformationOutlineFunction::IsApplicable( if (input_id_to_fresh_id_map.count(id) == 0) { return false; } - // Furthermore, no region input id is allowed to be the result of an access - // chain. This is because region input ids will become function parameters, - // and it is not legal to pass an access chain as a function parameter. - if (context->get_def_use_mgr()->GetDef(id)->opcode() == SpvOpAccessChain) { - return false; + // Furthermore, if the input id has pointer type it must be an OpVariable + // or OpFunctionParameter. + auto input_id_inst = context->get_def_use_mgr()->GetDef(id); + if (context->get_def_use_mgr() + ->GetDef(input_id_inst->type_id()) + ->opcode() == SpvOpTypePointer) { + switch (input_id_inst->opcode()) { + case SpvOpFunctionParameter: + case SpvOpVariable: + // These are OK. + break; + default: + // Anything else is not OK. + return false; + } } } diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index 7342dd426a..0d202b7a98 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -195,14 +195,15 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) { FactManager fact_manager; - FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + auto prng = MakeUnique(0); + FuzzerContext fuzzer_context(prng.get(), 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager, &fuzzer_context, &transformation_sequence, {}); - fuzzer_pass.DonateSingleModule(donor_context.get()); + fuzzer_pass.DonateSingleModule(donor_context.get(), false); // We just check that the result is valid. Checking to what it should be // exactly equal to would be very fragile. @@ -276,7 +277,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { &fuzzer_context, &transformation_sequence, {}); - fuzzer_pass.DonateSingleModule(donor_context.get()); + fuzzer_pass.DonateSingleModule(donor_context.get(), false); ASSERT_TRUE(IsValid(env, recipient_context.get())); @@ -397,7 +398,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { &fuzzer_context, &transformation_sequence, {}); - fuzzer_pass.DonateSingleModule(donor_context.get()); + fuzzer_pass.DonateSingleModule(donor_context.get(), false); ASSERT_TRUE(IsValid(env, recipient_context.get())); @@ -483,7 +484,7 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { &fuzzer_context, &transformation_sequence, {}); - fuzzer_pass.DonateSingleModule(donor_context.get()); + fuzzer_pass.DonateSingleModule(donor_context.get(), false); // We just check that the result is valid. Checking to what it should be // exactly equal to would be very fragile. @@ -660,7 +661,7 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { &fuzzer_context, &transformation_sequence, {}); - fuzzer_pass.DonateSingleModule(donor_context.get()); + fuzzer_pass.DonateSingleModule(donor_context.get(), false); // We just check that the result is valid. Checking to what it should be // exactly equal to would be very fragile. diff --git a/test/fuzz/transformation_add_dead_break_test.cpp b/test/fuzz/transformation_add_dead_break_test.cpp index 1dd0c9d4e9..d60fc1fc52 100644 --- a/test/fuzz/transformation_add_dead_break_test.cpp +++ b/test/fuzz/transformation_add_dead_break_test.cpp @@ -1948,9 +1948,6 @@ TEST(TransformationAddDeadBreakTest, PhiInstructions) { // Not applicable because two OpPhis (not just one) need to be updated at 20 ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13}) .IsApplicable(context.get(), fact_manager)); - // Not applicable because only two OpPhis (not three) need to be updated at 20 - ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13, 21, 22}) - .IsApplicable(context.get(), fact_manager)); // Not applicable because the given ids do not have types that match the // OpPhis at 20, in order ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 13}) diff --git a/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp index 66130be2bc..3bc7620510 100644 --- a/test/fuzz/transformation_add_function_test.cpp +++ b/test/fuzz/transformation_add_function_test.cpp @@ -20,6 +20,44 @@ namespace spvtools { namespace fuzz { namespace { +protobufs::AccessChainClampingInfo MakeAccessClampingInfo( + uint32_t access_chain_id, + const std::vector>& compare_and_select_ids) { + protobufs::AccessChainClampingInfo result; + result.set_access_chain_id(access_chain_id); + for (auto& compare_and_select_id : compare_and_select_ids) { + auto pair = result.add_compare_and_select_ids(); + pair->set_first(compare_and_select_id.first); + pair->set_second(compare_and_select_id.second); + } + return result; +} + +std::vector GetInstructionsForFunction( + spv_target_env env, const MessageConsumer& consumer, + const std::string& donor, uint32_t function_id) { + std::vector result; + const auto donor_context = + BuildModule(env, consumer, donor, kFuzzAssembleOption); + assert(IsValid(env, donor_context.get()) && "The given donor must be valid."); + for (auto& function : *donor_context->module()) { + if (function.result_id() == function_id) { + function.ForEachInst([&result](opt::Instruction* inst) { + opt::Instruction::OperandList input_operands; + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + input_operands.push_back(inst->GetInOperand(i)); + } + result.push_back(MakeInstructionMessage(inst->opcode(), inst->type_id(), + inst->result_id(), + input_operands)); + }); + break; + } + } + assert(!result.empty() && "The required function should have been found."); + return result; +} + TEST(TransformationAddFunctionTest, BasicTest) { std::string shader = R"( OpCapability Shader @@ -190,6 +228,12 @@ TEST(TransformationAddFunctionTest, BasicTest) { OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation1, context.get())); + ASSERT_TRUE(fact_manager.BlockIsDead(14)); + ASSERT_TRUE(fact_manager.BlockIsDead(21)); + ASSERT_TRUE(fact_manager.BlockIsDead(22)); + ASSERT_TRUE(fact_manager.BlockIsDead(23)); + ASSERT_TRUE(fact_manager.BlockIsDead(24)); + ASSERT_TRUE(fact_manager.BlockIsDead(25)); TransformationAddFunction transformation2(std::vector( {MakeInstructionMessage( @@ -320,6 +364,7 @@ TEST(TransformationAddFunctionTest, BasicTest) { OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation2, context.get())); + ASSERT_TRUE(fact_manager.BlockIsDead(16)); } TEST(TransformationAddFunctionTest, InapplicableTransformations) { @@ -442,6 +487,2261 @@ TEST(TransformationAddFunctionTest, InapplicableTransformations) { .IsApplicable(context.get(), fact_manager)); } +TEST(TransformationAddFunctionTest, LoopLimiters) { + 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 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 30, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 31, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 20, {})); + instructions.push_back(MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {21}}, + {SPV_OPERAND_TYPE_ID, {22}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {12}}, + {SPV_OPERAND_TYPE_ID, {23}}, + {SPV_OPERAND_TYPE_ID, {21}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 23, {})); + instructions.push_back(MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {25}}, + {SPV_OPERAND_TYPE_ID, {26}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {28}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 28, {})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {12}}, + {SPV_OPERAND_TYPE_ID, {26}}, + {SPV_OPERAND_TYPE_ID, {25}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 26, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {23}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 25, {})); + instructions.push_back(MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {24}}, + {SPV_OPERAND_TYPE_ID, {27}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {12}}, + {SPV_OPERAND_TYPE_ID, {24}}, + {SPV_OPERAND_TYPE_ID, {27}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 27, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 24, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {22}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 22, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 21, {})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + FactManager fact_manager; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context1.get())); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); + add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context1.get())); + // The added function should not be deemed livesafe. + ASSERT_FALSE(fact_manager.FunctionIsLivesafe(30)); + + std::string added_as_dead_code = 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 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %3 + %31 = OpLabel + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None + OpBranchConditional %12 %23 %21 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + OpLoopMerge %24 %27 None + OpBranchConditional %12 %24 %27 + %27 = OpLabel + OpBranch %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + protobufs::LoopLimiterInfo loop_limiter1; + loop_limiter1.set_loop_header_id(20); + loop_limiter1.set_load_id(101); + loop_limiter1.set_increment_id(102); + loop_limiter1.set_compare_id(103); + loop_limiter1.set_logical_op_id(104); + + protobufs::LoopLimiterInfo loop_limiter2; + loop_limiter2.set_loop_header_id(23); + loop_limiter2.set_load_id(105); + loop_limiter2.set_increment_id(106); + loop_limiter2.set_compare_id(107); + loop_limiter2.set_logical_op_id(108); + + protobufs::LoopLimiterInfo loop_limiter3; + loop_limiter3.set_loop_header_id(25); + loop_limiter3.set_load_id(109); + loop_limiter3.set_increment_id(110); + loop_limiter3.set_compare_id(111); + loop_limiter3.set_logical_op_id(112); + + std::vector loop_limiters = { + loop_limiter1, loop_limiter2, loop_limiter3}; + + TransformationAddFunction add_livesafe_function(instructions, 100, 10, + loop_limiters, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); + add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context2.get())); + // The added function should indeed be deemed livesafe. + ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30)); + std::string added_as_livesafe_code = 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 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %3 + %31 = OpLabel + %100 = OpVariable %7 Function %8 + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None + OpBranchConditional %12 %23 %21 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + %105 = OpLoad %6 %100 + %106 = OpIAdd %6 %105 %9 + OpStore %100 %106 + %107 = OpUGreaterThanEqual %11 %105 %10 + OpBranchConditional %107 %25 %23 + %25 = OpLabel + OpLoopMerge %24 %27 None + OpBranchConditional %12 %24 %27 + %27 = OpLabel + %109 = OpLoad %6 %100 + %110 = OpIAdd %6 %109 %9 + OpStore %100 %110 + %111 = OpUGreaterThanEqual %11 %109 %10 + OpBranchConditional %111 %24 %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + %101 = OpLoad %6 %100 + %102 = OpIAdd %6 %101 %9 + OpStore %100 %102 + %103 = OpUGreaterThanEqual %11 %101 %10 + OpBranchConditional %103 %21 %20 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { + 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 10, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIEqual, 14, 15, + {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpSelectionMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {15}}, + {SPV_OPERAND_TYPE_ID, {16}}, + {SPV_OPERAND_TYPE_ID, {17}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {})); + instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {})); + instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + FactManager fact_manager; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context1.get())); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); + add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context1.get())); + // The added function should not be deemed livesafe. + ASSERT_FALSE(fact_manager.FunctionIsLivesafe(10)); + + std::string added_as_dead_code = 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpUnreachable + %17 = OpLabel + OpKill + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, + {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); + add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context2.get())); + // The added function should indeed be deemed livesafe. + ASSERT_TRUE(fact_manager.FunctionIsLivesafe(10)); + std::string added_as_livesafe_code = 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpReturn + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { + 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpTypeFunction %6 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 6, 10, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {50}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIEqual, 14, 15, + {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpSelectionMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {15}}, + {SPV_OPERAND_TYPE_ID, {16}}, + {SPV_OPERAND_TYPE_ID, {17}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {})); + instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {})); + instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + FactManager fact_manager; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context1.get())); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); + add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context1.get())); + // The added function should not be deemed livesafe. + ASSERT_FALSE(fact_manager.FunctionIsLivesafe(10)); + + std::string added_as_dead_code = 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpTypeFunction %6 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %50 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpUnreachable + %17 = OpLabel + OpKill + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, + {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); + add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context2.get())); + // The added function should indeed be deemed livesafe. + ASSERT_TRUE(fact_manager.FunctionIsLivesafe(10)); + std::string added_as_livesafe_code = 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpTypeFunction %6 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %50 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpReturnValue %13 + %17 = OpLabel + OpReturnValue %13 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, ClampedAccessChains) { + 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 + %100 = OpTypeBool + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %15 = OpTypeInt 32 0 + %102 = OpTypePointer Function %15 + %8 = OpTypeFunction %2 %7 %102 %7 + %16 = OpConstant %15 5 + %17 = OpTypeArray %6 %16 + %18 = OpTypeArray %17 %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %19 Private + %21 = OpConstant %6 0 + %23 = OpTypePointer Private %6 + %26 = OpTypePointer Function %17 + %29 = OpTypePointer Private %17 + %33 = OpConstant %6 4 + %200 = OpConstant %15 4 + %35 = OpConstant %15 10 + %36 = OpTypeArray %6 %35 + %37 = OpTypePointer Private %36 + %38 = OpVariable %37 Private + %54 = OpTypeFloat 32 + %55 = OpTypeVector %54 4 + %56 = OpTypePointer Private %55 + %57 = OpVariable %56 Private + %59 = OpTypeVector %54 3 + %60 = OpTypeMatrix %59 2 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %64 = OpTypePointer Private %54 + %69 = OpConstant %54 2 + %71 = OpConstant %6 1 + %72 = OpConstant %6 2 + %201 = OpConstant %15 2 + %73 = OpConstant %6 3 + %202 = OpConstant %15 3 + %203 = OpConstant %6 1 + %204 = OpConstant %6 9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 12, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 102, 10, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 13, {})); + + instructions.push_back(MakeInstructionMessage( + SpvOpVariable, 7, 14, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpVariable, 26, 27, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 22, {{SPV_OPERAND_TYPE_ID, {11}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 24, + {{SPV_OPERAND_TYPE_ID, {20}}, + {SPV_OPERAND_TYPE_ID, {21}}, + {SPV_OPERAND_TYPE_ID, {22}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 25, {{SPV_OPERAND_TYPE_ID, {24}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 28, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 29, 30, + {{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 17, 31, {{SPV_OPERAND_TYPE_ID, {30}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 32, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpInBoundsAccessChain, 7, 34, + {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 39, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 23, 40, + {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 41, {{SPV_OPERAND_TYPE_ID, {40}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpInBoundsAccessChain, 23, 42, + {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 43, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 44, {{SPV_OPERAND_TYPE_ID, {11}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 45, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 46, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIAdd, 6, 47, + {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 23, 48, + {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 49, {{SPV_OPERAND_TYPE_ID, {48}}})); + instructions.push_back(MakeInstructionMessage(SpvOpInBoundsAccessChain, 23, + 50, + {{SPV_OPERAND_TYPE_ID, {20}}, + {SPV_OPERAND_TYPE_ID, {43}}, + {SPV_OPERAND_TYPE_ID, {44}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 51, {{SPV_OPERAND_TYPE_ID, {50}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIAdd, 6, 52, + {{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 53, + {{SPV_OPERAND_TYPE_ID, {20}}, + {SPV_OPERAND_TYPE_ID, {43}}, + {SPV_OPERAND_TYPE_ID, {44}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 58, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 63, {{SPV_OPERAND_TYPE_ID, {11}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 65, + {{SPV_OPERAND_TYPE_ID, {62}}, + {SPV_OPERAND_TYPE_ID, {21}}, + {SPV_OPERAND_TYPE_ID, {63}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 101, + {{SPV_OPERAND_TYPE_ID, {62}}, + {SPV_OPERAND_TYPE_ID, {45}}, + {SPV_OPERAND_TYPE_ID, {46}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 54, 66, {{SPV_OPERAND_TYPE_ID, {65}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 64, 67, + {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 68, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpInBoundsAccessChain, 64, 70, + {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + FactManager fact_manager; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context1.get())); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); + add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context1.get())); + + std::string added_as_dead_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %100 = OpTypeBool + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %15 = OpTypeInt 32 0 + %102 = OpTypePointer Function %15 + %8 = OpTypeFunction %2 %7 %102 %7 + %16 = OpConstant %15 5 + %17 = OpTypeArray %6 %16 + %18 = OpTypeArray %17 %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %19 Private + %21 = OpConstant %6 0 + %23 = OpTypePointer Private %6 + %26 = OpTypePointer Function %17 + %29 = OpTypePointer Private %17 + %33 = OpConstant %6 4 + %200 = OpConstant %15 4 + %35 = OpConstant %15 10 + %36 = OpTypeArray %6 %35 + %37 = OpTypePointer Private %36 + %38 = OpVariable %37 Private + %54 = OpTypeFloat 32 + %55 = OpTypeVector %54 4 + %56 = OpTypePointer Private %55 + %57 = OpVariable %56 Private + %59 = OpTypeVector %54 3 + %60 = OpTypeMatrix %59 2 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %64 = OpTypePointer Private %54 + %69 = OpConstant %54 2 + %71 = OpConstant %6 1 + %72 = OpConstant %6 2 + %201 = OpConstant %15 2 + %73 = OpConstant %6 3 + %202 = OpConstant %15 3 + %203 = OpConstant %6 1 + %204 = OpConstant %6 9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %102 + %11 = OpFunctionParameter %7 + %13 = OpLabel + %14 = OpVariable %7 Function + %27 = OpVariable %26 Function + %22 = OpLoad %6 %11 + %24 = OpAccessChain %23 %20 %21 %22 + %25 = OpLoad %6 %24 + OpStore %14 %25 + %28 = OpLoad %15 %10 + %30 = OpAccessChain %29 %20 %28 + %31 = OpLoad %17 %30 + OpStore %27 %31 + %32 = OpLoad %6 %9 + %34 = OpInBoundsAccessChain %7 %27 %32 + OpStore %34 %33 + %39 = OpLoad %6 %9 + %40 = OpAccessChain %23 %38 %33 + %41 = OpLoad %6 %40 + %42 = OpInBoundsAccessChain %23 %38 %39 + OpStore %42 %41 + %43 = OpLoad %15 %10 + %44 = OpLoad %6 %11 + %45 = OpLoad %6 %9 + %46 = OpLoad %15 %10 + %47 = OpIAdd %6 %45 %46 + %48 = OpAccessChain %23 %38 %47 + %49 = OpLoad %6 %48 + %50 = OpInBoundsAccessChain %23 %20 %43 %44 + %51 = OpLoad %6 %50 + %52 = OpIAdd %6 %51 %49 + %53 = OpAccessChain %23 %20 %43 %44 + OpStore %53 %52 + %58 = OpLoad %15 %10 + %63 = OpLoad %6 %11 + %65 = OpAccessChain %64 %62 %21 %63 + %101 = OpAccessChain %64 %62 %45 %46 + %66 = OpLoad %54 %65 + %67 = OpAccessChain %64 %57 %58 + OpStore %67 %66 + %68 = OpLoad %6 %9 + %70 = OpInBoundsAccessChain %64 %57 %68 + OpStore %70 %69 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + std::vector access_chain_clamping_info; + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(24, {{1001, 2001}, {1002, 2002}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(30, {{1003, 2003}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(34, {{1004, 2004}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(40, {{1005, 2005}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(42, {{1006, 2006}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(48, {{1007, 2007}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(50, {{1008, 2008}, {1009, 2009}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(53, {{1010, 2010}, {1011, 2011}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(65, {{1012, 2012}, {1013, 2013}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(101, {{1014, 2014}, {1015, 2015}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(67, {{1016, 2016}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(70, {{1017, 2017}})); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, + access_chain_clamping_info); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); + add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context2.get())); + std::string added_as_livesafe_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %100 = OpTypeBool + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %15 = OpTypeInt 32 0 + %102 = OpTypePointer Function %15 + %8 = OpTypeFunction %2 %7 %102 %7 + %16 = OpConstant %15 5 + %17 = OpTypeArray %6 %16 + %18 = OpTypeArray %17 %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %19 Private + %21 = OpConstant %6 0 + %23 = OpTypePointer Private %6 + %26 = OpTypePointer Function %17 + %29 = OpTypePointer Private %17 + %33 = OpConstant %6 4 + %200 = OpConstant %15 4 + %35 = OpConstant %15 10 + %36 = OpTypeArray %6 %35 + %37 = OpTypePointer Private %36 + %38 = OpVariable %37 Private + %54 = OpTypeFloat 32 + %55 = OpTypeVector %54 4 + %56 = OpTypePointer Private %55 + %57 = OpVariable %56 Private + %59 = OpTypeVector %54 3 + %60 = OpTypeMatrix %59 2 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %64 = OpTypePointer Private %54 + %69 = OpConstant %54 2 + %71 = OpConstant %6 1 + %72 = OpConstant %6 2 + %201 = OpConstant %15 2 + %73 = OpConstant %6 3 + %202 = OpConstant %15 3 + %203 = OpConstant %6 1 + %204 = OpConstant %6 9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %102 + %11 = OpFunctionParameter %7 + %13 = OpLabel + %14 = OpVariable %7 Function + %27 = OpVariable %26 Function + %22 = OpLoad %6 %11 + %1002 = OpULessThanEqual %100 %22 %33 + %2002 = OpSelect %6 %1002 %22 %33 + %24 = OpAccessChain %23 %20 %21 %2002 + %25 = OpLoad %6 %24 + OpStore %14 %25 + %28 = OpLoad %15 %10 + %1003 = OpULessThanEqual %100 %28 %200 + %2003 = OpSelect %15 %1003 %28 %200 + %30 = OpAccessChain %29 %20 %2003 + %31 = OpLoad %17 %30 + OpStore %27 %31 + %32 = OpLoad %6 %9 + %1004 = OpULessThanEqual %100 %32 %33 + %2004 = OpSelect %6 %1004 %32 %33 + %34 = OpInBoundsAccessChain %7 %27 %2004 + OpStore %34 %33 + %39 = OpLoad %6 %9 + %40 = OpAccessChain %23 %38 %33 + %41 = OpLoad %6 %40 + %1006 = OpULessThanEqual %100 %39 %204 + %2006 = OpSelect %6 %1006 %39 %204 + %42 = OpInBoundsAccessChain %23 %38 %2006 + OpStore %42 %41 + %43 = OpLoad %15 %10 + %44 = OpLoad %6 %11 + %45 = OpLoad %6 %9 + %46 = OpLoad %15 %10 + %47 = OpIAdd %6 %45 %46 + %1007 = OpULessThanEqual %100 %47 %204 + %2007 = OpSelect %6 %1007 %47 %204 + %48 = OpAccessChain %23 %38 %2007 + %49 = OpLoad %6 %48 + %1008 = OpULessThanEqual %100 %43 %200 + %2008 = OpSelect %15 %1008 %43 %200 + %1009 = OpULessThanEqual %100 %44 %33 + %2009 = OpSelect %6 %1009 %44 %33 + %50 = OpInBoundsAccessChain %23 %20 %2008 %2009 + %51 = OpLoad %6 %50 + %52 = OpIAdd %6 %51 %49 + %1010 = OpULessThanEqual %100 %43 %200 + %2010 = OpSelect %15 %1010 %43 %200 + %1011 = OpULessThanEqual %100 %44 %33 + %2011 = OpSelect %6 %1011 %44 %33 + %53 = OpAccessChain %23 %20 %2010 %2011 + OpStore %53 %52 + %58 = OpLoad %15 %10 + %63 = OpLoad %6 %11 + %1013 = OpULessThanEqual %100 %63 %72 + %2013 = OpSelect %6 %1013 %63 %72 + %65 = OpAccessChain %64 %62 %21 %2013 + %1014 = OpULessThanEqual %100 %45 %71 + %2014 = OpSelect %6 %1014 %45 %71 + %1015 = OpULessThanEqual %100 %46 %201 + %2015 = OpSelect %15 %1015 %46 %201 + %101 = OpAccessChain %64 %62 %2014 %2015 + %66 = OpLoad %54 %65 + %1016 = OpULessThanEqual %100 %58 %202 + %2016 = OpSelect %15 %1016 %58 %202 + %67 = OpAccessChain %64 %57 %2016 + OpStore %67 %66 + %68 = OpLoad %6 %9 + %1017 = OpULessThanEqual %100 %68 %73 + %2017 = OpSelect %6 %1017 %68 %73 + %70 = OpInBoundsAccessChain %64 %57 %2017 + OpStore %70 %69 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { + 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 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 8, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11, + {{SPV_OPERAND_TYPE_ID, {6}}})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + FactManager fact_manager1; + FactManager fact_manager2; + + // Mark function 6 as livesafe. + fact_manager2.AddFactFunctionIsLivesafe(6); + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context1.get())); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); + add_dead_function.Apply(context1.get(), &fact_manager1); + ASSERT_TRUE(IsValid(env, context1.get())); + + std::string added_as_live_or_dead_code = 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 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %11 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, + {}); + ASSERT_TRUE( + add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); + add_livesafe_function.Apply(context2.get(), &fact_manager2); + ASSERT_TRUE(IsValid(env, context2.get())); + ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { + 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 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpKill + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 8, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11, + {{SPV_OPERAND_TYPE_ID, {6}}})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + FactManager fact_manager; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context1.get())); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); + add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context1.get())); + + std::string added_as_dead_code = 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 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpKill + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %11 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, + {}); + ASSERT_FALSE( + add_livesafe_function.IsApplicable(context2.get(), fact_manager)); +} + +TEST(TransformationAddFunctionTest, + LoopLimitersBackEdgeBlockEndsWithConditional1) { + 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + OpBranchConditional %20 %12 %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + FactManager fact_manager; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); + add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string expected = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %102 = OpLoad %28 %100 + %103 = OpIAdd %28 %102 %31 + OpStore %100 %103 + %104 = OpULessThan %19 %102 %32 + %105 = OpLogicalAnd %19 %20 %104 + OpBranchConditional %105 %12 %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, + LoopLimitersBackEdgeBlockEndsWithConditional2) { + 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + OpBranchConditional %50 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + FactManager fact_manager; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); + add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string expected = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + %102 = OpLoad %28 %100 + %103 = OpIAdd %28 %102 %31 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %19 %102 %32 + %105 = OpLogicalOr %19 %50 %104 + OpBranchConditional %105 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) { + 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + OpLoopMerge %14 %12 None + OpBranchConditional %50 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + FactManager fact_manager; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); + add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string expected = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + %102 = OpLoad %28 %100 + %103 = OpIAdd %28 %102 %31 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %19 %102 %32 + %105 = OpLogicalOr %19 %50 %104 + OpLoopMerge %14 %12 None + OpBranchConditional %105 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, InfiniteLoop1) { + 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %12 None + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + FactManager fact_manager; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); + add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string expected = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + %102 = OpLoad %28 %100 + %103 = OpIAdd %28 %102 %31 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %19 %102 %32 + OpLoopMerge %14 %12 None + OpBranchConditional %104 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) { + // This captures the case where the loop's continue construct is statically + // unreachable. In this case the loop cannot iterate and so we do not add + // a loop limiter. (The reason we do not just add one anyway is that + // detecting which block would be the back-edge block is difficult in the + // absence of reliable dominance information.) + 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %23 = OpConstant %8 1 + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %23 = OpConstant %8 1 + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + OpBranch %14 + %15 = OpLabel + %22 = OpLoad %8 %10 + %24 = OpIAdd %8 %22 %23 + OpStore %10 %24 + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + FactManager fact_manager; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); + add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string expected = 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 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %23 = OpConstant %8 1 + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + OpBranch %14 + %15 = OpLabel + %22 = OpLoad %8 %10 + %24 = OpIAdd %8 %22 %23 + OpStore %10 %24 + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) { + // This captures the scenario where breaking a loop due to a loop limiter + // requires patching up OpPhi instructions occurring at the loop merge block. + + 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 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = 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 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %36 = OpFunctionCall %6 %8 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + OpBranch %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 + OpReturnValue %38 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + FactManager fact_manager; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 8); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(13); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + + TransformationAddFunction no_op_phi_data(instructions, 100, 28, + {loop_limiter_info}, 0, {}); + // The loop limiter info is not good enough; it does not include ids to patch + // up the OpPhi at the loop merge. + ASSERT_FALSE(no_op_phi_data.IsApplicable(context.get(), fact_manager)); + + // Add a phi id for the new edge from the loop back edge block to the loop + // merge. + loop_limiter_info.add_phi_id(28); + TransformationAddFunction with_op_phi_data(instructions, 100, 28, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(with_op_phi_data.IsApplicable(context.get(), fact_manager)); + with_op_phi_data.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string expected = 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 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %100 = OpVariable %53 Function %51 + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + %102 = OpLoad %50 %100 + %103 = OpIAdd %50 %102 %52 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %20 %102 %28 + OpBranchConditional %104 %15 %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 %28 %16 + OpReturnValue %38 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) { + // This captures the scenario where the loop merge block already has an OpPhi + // with the loop back edge block as a predecessor. + + 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 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %60 = OpConstantTrue %20 + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = 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 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %60 = OpConstantTrue %20 + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + OpBranchConditional %60 %15 %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 %23 %16 + OpReturnValue %38 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + FactManager fact_manager; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 8); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(13); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + + TransformationAddFunction transformation(instructions, 100, 28, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + std::string expected = 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 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %60 = OpConstantTrue %20 + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %100 = OpVariable %53 Function %51 + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + %102 = OpLoad %50 %100 + %103 = OpIAdd %50 %102 %52 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %20 %102 %28 + %105 = OpLogicalOr %20 %60 %104 + OpBranchConditional %105 %15 %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 %23 %16 + OpReturnValue %38 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index 5cd1437675..91e17337f4 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -1656,6 +1656,64 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) { ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); } +TEST(TransformationOutlineFunctionTest, + DoNotOutlineRegionThatUsesCopiedObject) { + // Copying a variable leads to a pointer, but one that cannot be passed as a + // function parameter, as it is not a memory object. + 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 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %9 = OpTypePointer Function %6 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %8 Function + OpBranch %11 + %11 = OpLabel + %20 = OpCopyObject %8 %10 + OpBranch %13 + %13 = OpLabel + %12 = OpAccessChain %9 %20 %19 + %14 = OpLoad %6 %12 + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 13, + /*exit_block*/ 15, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{20, 207}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + TEST(TransformationOutlineFunctionTest, Miscellaneous1) { // This tests outlining of some non-trivial code. From dd3f8d4922ebd9b6275f68d7cfad5f34726ef462 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 29 Jan 2020 17:58:49 +0000 Subject: [PATCH 02/88] spirv-fuzz: Add outlining test (#3164) Adds a test to check that we do outline code that uses pointer parameters. --- .../transformation_outline_function_test.cpp | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index 91e17337f4..ff3d91314d 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -1714,6 +1714,118 @@ TEST(TransformationOutlineFunctionTest, ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); } +TEST(TransformationOutlineFunctionTest, + DoOutlineRegionThatUsesPointerParameter) { + // The region being outlined reads from a function parameter of pointer type. + // This is OK: the function parameter can itself be passed on as a function + // parameter. + 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpVariable %7 Function + %16 = OpVariable %7 Function + %17 = OpLoad %6 %15 + OpStore %16 %17 + %18 = OpFunctionCall %2 %10 %16 + %19 = OpLoad %6 %16 + OpStore %15 %19 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %14 = OpIAdd %6 %12 %13 + OpBranch %20 + %20 = OpLabel + OpStore %9 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 11, + /*exit_block*/ 11, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{9, 207}}, + /*output_id_to_fresh_id*/ {{14, 208}}); + + 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %200 = OpTypeStruct %6 + %201 = OpTypeFunction %200 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpVariable %7 Function + %16 = OpVariable %7 Function + %17 = OpLoad %6 %15 + OpStore %16 %17 + %18 = OpFunctionCall %2 %10 %16 + %19 = OpLoad %6 %16 + OpStore %15 %19 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %205 = OpFunctionCall %200 %202 %9 + %14 = OpCompositeExtract %6 %205 0 + OpBranch %20 + %20 = OpLabel + OpStore %9 %14 + OpReturn + OpFunctionEnd + %202 = OpFunction %200 None %201 + %207 = OpFunctionParameter %7 + %204 = OpLabel + %12 = OpLoad %6 %207 + %208 = OpIAdd %6 %12 %13 + %206 = OpCompositeConstruct %200 %208 + OpReturnValue %206 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + TEST(TransformationOutlineFunctionTest, Miscellaneous1) { // This tests outlining of some non-trivial code. From 1fc7a9ec77bc86e9953a99cb3f731e39f3e32b9d Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 30 Jan 2020 11:25:29 +0000 Subject: [PATCH 03/88] spirv-fuzz: Arbitrary variable facts (#3165) This change adds a new kind of fact to the fact manager, which records when a variable (or pointer parameter) refers to an arbitrary value, so that anything can be stored to it, without affecting the observable behaviour of the module, and nothing can be guaranteed about values loaded from it. Donated modules are the current source of such variables, and other transformations, such as outlining, have been adapted to propagate these facts appropriately. --- source/fuzz/fact_manager.cpp | 45 +- source/fuzz/fact_manager.h | 20 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 8 +- source/fuzz/protobufs/spvtoolsfuzz.proto | 21 + source/fuzz/transformation_add_function.cpp | 24 + .../transformation_add_global_variable.cpp | 10 +- .../fuzz/transformation_add_global_variable.h | 3 +- .../fuzz/transformation_outline_function.cpp | 23 +- source/fuzz/transformation_outline_function.h | 8 +- .../fuzz/transformation_add_function_test.cpp | 163 ++++-- ...ransformation_add_global_variable_test.cpp | 56 +- .../transformation_outline_function_test.cpp | 489 ++++++++++++++++++ 12 files changed, 805 insertions(+), 65 deletions(-) diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp index 9672653a10..a7b431120a 100644 --- a/source/fuzz/fact_manager.cpp +++ b/source/fuzz/fact_manager.cpp @@ -859,11 +859,43 @@ bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe( // End of livesafe function facts //============================== +//============================== +// Arbitrarily-valued variable facts + +// The purpose of this class is to group the fields and data used to represent +// facts about livesafe functions. +class FactManager::ArbitrarilyValuedVaribleFacts { + public: + // See method in FactManager which delegates to this method. + void AddFact(const protobufs::FactValueOfVariableIsArbitrary& fact); + + // See method in FactManager which delegates to this method. + bool VariableValueIsArbitrary(uint32_t variable_id) const; + + private: + std::set arbitrary_valued_varible_ids_; +}; + +void FactManager::ArbitrarilyValuedVaribleFacts::AddFact( + const protobufs::FactValueOfVariableIsArbitrary& fact) { + arbitrary_valued_varible_ids_.insert(fact.variable_id()); +} + +bool FactManager::ArbitrarilyValuedVaribleFacts::VariableValueIsArbitrary( + uint32_t variable_id) const { + return arbitrary_valued_varible_ids_.count(variable_id) != 0; +} + +// End of arbitrarily-valued variable facts +//============================== + FactManager::FactManager() : uniform_constant_facts_(MakeUnique()), data_synonym_facts_(MakeUnique()), dead_block_facts_(MakeUnique()), - livesafe_function_facts_(MakeUnique()) {} + livesafe_function_facts_(MakeUnique()), + arbitrarily_valued_variable_facts_( + MakeUnique()) {} FactManager::~FactManager() = default; @@ -985,5 +1017,16 @@ void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) { livesafe_function_facts_->AddFact(fact); } +bool FactManager::VariableValueIsArbitrary(uint32_t variable_id) const { + return arbitrarily_valued_variable_facts_->VariableValueIsArbitrary( + variable_id); +} + +void FactManager::AddFactValueOfVariableIsArbitrary(uint32_t variable_id) { + protobufs::FactValueOfVariableIsArbitrary fact; + fact.set_variable_id(variable_id); + arbitrarily_valued_variable_facts_->AddFact(fact); +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h index 20f270154f..117ed1c617 100644 --- a/source/fuzz/fact_manager.h +++ b/source/fuzz/fact_manager.h @@ -64,6 +64,10 @@ class FactManager { // Records the fact that |function_id| is livesafe. void AddFactFunctionIsLivesafe(uint32_t function_id); + // Records the fact that |variable_id| has an arbitrary value and can thus be + // stored to without affecting the module's behaviour. + void AddFactValueOfVariableIsArbitrary(uint32_t variable_id); + // The fact manager is responsible for managing a few distinct categories of // facts. In principle there could be different fact managers for each kind // of fact, but in practice providing one 'go to' place for facts is @@ -153,7 +157,16 @@ class FactManager { // to be livesafe. bool FunctionIsLivesafe(uint32_t function_id) const; - // End of dead block facts + // End of dead livesafe function facts + //============================== + + //============================== + // Querying facts about arbitrarily-valued variables + + // Returns true if and ony if |variable_id| is arbitrarily-valued. + bool VariableValueIsArbitrary(uint32_t variable_id) const; + + // End of arbitrarily-valued variable facts //============================== private: @@ -177,6 +190,11 @@ class FactManager { // function facts. std::unique_ptr livesafe_function_facts_; // Unique pointer to internal data. + + class ArbitrarilyValuedVaribleFacts; // Opaque class for management of + // facts about variables whose values should be expected to be arbitrary. + std::unique_ptr + arbitrarily_valued_variable_facts_; // Unique pointer to internal data. }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index b0b9d27ff8..75530b10e3 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -396,6 +396,11 @@ void FuzzerPassDonateModules::HandleTypesAndValues( // have storage class private. As a result, we simply add a Private // storage class global variable, using remapped versions of the result // type and initializer ids for the global variable in the donor. + // + // We regard the added variable as having an arbitrary value. This + // means that future passes can add stores to the variable in any + // way they wish, and pass them as pointer parameters to functions + // without worrying about whether their data might get modified. new_result_id = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationAddGlobalVariable( new_result_id, @@ -403,7 +408,8 @@ void FuzzerPassDonateModules::HandleTypesAndValues( type_or_value.NumInOperands() == 1 ? 0 : original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(1)))); + type_or_value.GetSingleWordInOperand(1)), + true)); } break; case SpvOpUndef: { // It is fine to have multiple Undef instructions of the same type, so diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 2f37a7d967..52a3a788ca 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -168,6 +168,7 @@ message Fact { FactDataSynonym data_synonym_fact = 2; FactBlockIsDead block_is_dead_fact = 3; FactFunctionIsLivesafe function_is_livesafe_fact = 4; + FactValueOfVariableIsArbitrary value_of_variable_is_arbitrary = 5; } } @@ -222,6 +223,19 @@ message FactFunctionIsLivesafe { uint32 function_id = 1; } +message FactValueOfVariableIsArbitrary { + + // Records the fact that the value stored in the variable or function + // parameter with the given id can be arbitrary: the module's observable + // behaviour does not depend on it. This means that arbitrary stores can be + // made to the variable, and that nothing can be guaranteed about values + // loaded from the variable. + + // The result id of an OpVariable instruction, or an OpFunctionParameter + // instruction with pointer type + uint32 variable_id = 1; +} + message AccessChainClampingInfo { // When making a function livesafe it is necessary to clamp the indices that @@ -493,6 +507,13 @@ message TransformationAddGlobalVariable { // Optional initializer; 0 if there is no initializer uint32 initializer_id = 3; + // True if and only if the value of the variable should be regarded, in + // general, as totally unknown and subject to change (even if, due to an + // initializer, the original value is known). This is the case for variables + // added when a module is donated, for example, and means that stores to such + // variables can be performed in an arbitrary fashion. + bool value_is_arbitrary = 4; + } message TransformationAddNoContractionDecoration { diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp index 8b8b2ddd50..b013d9441c 100644 --- a/source/fuzz/transformation_add_function.cpp +++ b/source/fuzz/transformation_add_function.cpp @@ -154,6 +154,30 @@ void TransformationAddFunction::Apply( (void)(success); // Keep release builds happy (otherwise they may complain // that |success| is not used). + // Record the fact that all pointer parameters and variables declared in the + // function should be regarded as having arbitrary values. This allows other + // passes to store arbitrarily to such variables, and to pass them freely as + // parameters to other functions knowing that it is OK if they get + // over-written. + for (auto& instruction : message_.instruction()) { + switch (instruction.opcode()) { + case SpvOpFunctionParameter: + if (context->get_def_use_mgr() + ->GetDef(instruction.result_type_id()) + ->opcode() == SpvOpTypePointer) { + fact_manager->AddFactValueOfVariableIsArbitrary( + instruction.result_id()); + } + break; + case SpvOpVariable: + fact_manager->AddFactValueOfVariableIsArbitrary( + instruction.result_id()); + break; + default: + break; + } + } + if (message_.is_livesafe()) { // Make the function livesafe, which also should succeed. success = TryToMakeFunctionLivesafe(context, *fact_manager); diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp index cea268c638..c08517f9a5 100644 --- a/source/fuzz/transformation_add_global_variable.cpp +++ b/source/fuzz/transformation_add_global_variable.cpp @@ -24,10 +24,12 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable( : message_(message) {} TransformationAddGlobalVariable::TransformationAddGlobalVariable( - uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id) { + uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id, + bool value_is_arbitrary) { message_.set_fresh_id(fresh_id); message_.set_type_id(type_id); message_.set_initializer_id(initializer_id); + message_.set_value_is_arbitrary(value_is_arbitrary); } bool TransformationAddGlobalVariable::IsApplicable( @@ -71,7 +73,7 @@ bool TransformationAddGlobalVariable::IsApplicable( } void TransformationAddGlobalVariable::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { opt::Instruction::OperandList input_operands; input_operands.push_back( {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}}); @@ -99,6 +101,10 @@ void TransformationAddGlobalVariable::Apply( } } + if (message_.value_is_arbitrary()) { + fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id()); + } + // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h index ca63e689a2..406c915715 100644 --- a/source/fuzz/transformation_add_global_variable.h +++ b/source/fuzz/transformation_add_global_variable.h @@ -29,7 +29,8 @@ class TransformationAddGlobalVariable : public Transformation { const protobufs::TransformationAddGlobalVariable& message); TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id, - uint32_t initializer_id); + uint32_t initializer_id, + bool value_is_arbitrary); // - |message_.fresh_id| must be fresh // - |message_.type_id| must be the id of a pointer type with Private storage diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index c097e6cfe6..7bbac54839 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -340,8 +340,16 @@ void TransformationOutlineFunction::Apply( // Make a function prototype for the outlined function, which involves // figuring out its required type. - std::unique_ptr outlined_function = PrepareFunctionPrototype( - context, region_input_ids, region_output_ids, input_id_to_fresh_id_map); + std::unique_ptr outlined_function = + PrepareFunctionPrototype(region_input_ids, region_output_ids, + input_id_to_fresh_id_map, context, fact_manager); + + // If the original function was livesafe, the new function should also be + // livesafe. + if (fact_manager->FunctionIsLivesafe( + original_region_entry_block->GetParent()->result_id())) { + fact_manager->AddFactFunctionIsLivesafe(message_.new_function_id()); + } // Adapt the region to be outlined so that its input ids are replaced with the // ids of the outlined function's input parameters, and so that output ids @@ -524,9 +532,10 @@ std::set TransformationOutlineFunction::GetRegionBlocks( std::unique_ptr TransformationOutlineFunction::PrepareFunctionPrototype( - opt::IRContext* context, const std::vector& region_input_ids, + const std::vector& region_input_ids, const std::vector& region_output_ids, - const std::map& input_id_to_fresh_id_map) const { + const std::map& input_id_to_fresh_id_map, + opt::IRContext* context, FactManager* fact_manager) const { uint32_t return_type_id = 0; uint32_t function_type_id = 0; @@ -608,6 +617,12 @@ TransformationOutlineFunction::PrepareFunctionPrototype( context, SpvOpFunctionParameter, context->get_def_use_mgr()->GetDef(id)->type_id(), input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList())); + // If the input id is an arbitrary-valued variable, the same should be true + // of the corresponding parameter. + if (fact_manager->VariableValueIsArbitrary(id)) { + fact_manager->AddFactValueOfVariableIsArbitrary( + input_id_to_fresh_id_map.at(id)); + } } return outlined_function; diff --git a/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h index 43bdf3b85c..5711790620 100644 --- a/source/fuzz/transformation_outline_function.h +++ b/source/fuzz/transformation_outline_function.h @@ -158,10 +158,14 @@ class TransformationOutlineFunction : public Transformation { // A new struct type to represent the function return type, and a new function // type for the function, will be added to the module (unless suitable types // are already present). + // + // Facts about the function containing the outlined region that are relevant + // to the new function are propagated via |fact_manager|. std::unique_ptr PrepareFunctionPrototype( - opt::IRContext* context, const std::vector& region_input_ids, + const std::vector& region_input_ids, const std::vector& region_output_ids, - const std::map& input_id_to_fresh_id_map) const; + const std::map& input_id_to_fresh_id_map, + opt::IRContext* context, FactManager* fact_manager) const; // Creates the body of the outlined function by cloning blocks from the // original region, given by |region_blocks|, adapting the cloned version diff --git a/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp index 3bc7620510..040d27ca5e 100644 --- a/test/fuzz/transformation_add_function_test.cpp +++ b/test/fuzz/transformation_add_function_test.cpp @@ -58,6 +58,56 @@ std::vector GetInstructionsForFunction( return result; } +// Returns true if and only if every pointer parameter and variable associated +// with |function_id| in |context| is known by |fact_manager| to be arbitrary, +// with the exception of |loop_limiter_id|, which must not be arbitrary. (It +// can be 0 if no loop limiter is expected, and 0 should not be deemed +// arbitrary). +bool AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + opt::IRContext* context, const FactManager& fact_manager, + uint32_t function_id, uint32_t loop_limiter_id) { + // Look at all the functions until the function of interest is found. + for (auto& function : *context->module()) { + if (function.result_id() != function_id) { + continue; + } + // Check that the parameters are all arbitrary. + bool found_non_arbitrary_parameter = false; + function.ForEachParam( + [context, &fact_manager, + &found_non_arbitrary_parameter](opt::Instruction* inst) { + if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == + SpvOpTypePointer && + !fact_manager.VariableValueIsArbitrary(inst->result_id())) { + found_non_arbitrary_parameter = true; + } + }); + if (found_non_arbitrary_parameter) { + // A non-arbitrary parameter was found. + return false; + } + // Look through the instructions in the function's first block. + for (auto& inst : *function.begin()) { + if (inst.opcode() != SpvOpVariable) { + // We have found a non-variable instruction; this means we have gotten + // past all variables, so we are done. + return true; + } + // The variable should be arbitrary if and only if it is not the loop + // limiter. + if ((inst.result_id() == loop_limiter_id) == + fact_manager.VariableValueIsArbitrary(inst.result_id())) { + return false; + } + } + assert(false && + "We should have processed all variables and returned by " + "this point."); + } + assert(false && "We should have found the function of interest."); + return true; +} + TEST(TransformationAddFunctionTest, BasicTest) { std::string shader = R"( OpCapability Shader @@ -570,18 +620,22 @@ TEST(TransformationAddFunctionTest, LoopLimiters) { instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); - FactManager fact_manager; + FactManager fact_manager1; + FactManager fact_manager2; const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); - add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); + add_dead_function.Apply(context1.get(), &fact_manager1); ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. - ASSERT_FALSE(fact_manager.FunctionIsLivesafe(30)); + ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(30)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context1.get(), fact_manager1, 30, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -657,11 +711,16 @@ TEST(TransformationAddFunctionTest, LoopLimiters) { TransformationAddFunction add_livesafe_function(instructions, 100, 10, loop_limiters, 0, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); - add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE( + add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); + add_livesafe_function.Apply(context2.get(), &fact_manager2); ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. - ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30)); + ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(30)); + // All variables/parameters in the function should be deemed arbitrary, + // except the loop limiter. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context2.get(), fact_manager2, 30, 100)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -776,18 +835,22 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); - FactManager fact_manager; + FactManager fact_manager1; + FactManager fact_manager2; const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); - add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); + add_dead_function.Apply(context1.get(), &fact_manager1); ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. - ASSERT_FALSE(fact_manager.FunctionIsLivesafe(10)); + ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context1.get(), fact_manager1, 10, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -824,11 +887,15 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); - add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE( + add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); + add_livesafe_function.Apply(context2.get(), &fact_manager2); ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. - ASSERT_TRUE(fact_manager.FunctionIsLivesafe(10)); + ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context2.get(), fact_manager2, 10, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -916,18 +983,22 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); - FactManager fact_manager; + FactManager fact_manager1; + FactManager fact_manager2; const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); - add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); + add_dead_function.Apply(context1.get(), &fact_manager1); ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. - ASSERT_FALSE(fact_manager.FunctionIsLivesafe(10)); + ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context1.get(), fact_manager1, 10, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -965,11 +1036,15 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); - add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE( + add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); + add_livesafe_function.Apply(context2.get(), &fact_manager2); ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. - ASSERT_TRUE(fact_manager.FunctionIsLivesafe(10)); + ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context2.get(), fact_manager2, 10, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -1188,16 +1263,22 @@ TEST(TransformationAddFunctionTest, ClampedAccessChains) { instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); - FactManager fact_manager; + FactManager fact_manager1; + FactManager fact_manager2; const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); - add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); + add_dead_function.Apply(context1.get(), &fact_manager1); ASSERT_TRUE(IsValid(env, context1.get())); + // The function should not be deemed livesafe + ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(12)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context1.get(), fact_manager1, 12, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -1328,9 +1409,15 @@ TEST(TransformationAddFunctionTest, ClampedAccessChains) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, access_chain_clamping_info); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager)); - add_livesafe_function.Apply(context2.get(), &fact_manager); + ASSERT_TRUE( + add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); + add_livesafe_function.Apply(context2.get(), &fact_manager2); ASSERT_TRUE(IsValid(env, context2.get())); + // The function should be deemed livesafe + ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(12)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context2.get(), fact_manager2, 12, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -1510,6 +1597,11 @@ TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); add_dead_function.Apply(context1.get(), &fact_manager1); ASSERT_TRUE(IsValid(env, context1.get())); + // The function should not be deemed livesafe + ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context1.get(), fact_manager1, 8, 0)); std::string added_as_live_or_dead_code = R"( OpCapability Shader @@ -1542,6 +1634,11 @@ TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); add_livesafe_function.Apply(context2.get(), &fact_manager2); ASSERT_TRUE(IsValid(env, context2.get())); + // The function should be deemed livesafe + ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(8)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context2.get(), fact_manager2, 8, 0)); ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get())); } @@ -1580,16 +1677,22 @@ TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); - FactManager fact_manager; + FactManager fact_manager1; + FactManager fact_manager2; const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager)); - add_dead_function.Apply(context1.get(), &fact_manager); + ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); + add_dead_function.Apply(context1.get(), &fact_manager1); ASSERT_TRUE(IsValid(env, context1.get())); + // The function should not be deemed livesafe + ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); + // All variables/parameters in the function should be deemed arbitrary. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + context1.get(), fact_manager1, 8, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -1619,7 +1722,7 @@ TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, {}); ASSERT_FALSE( - add_livesafe_function.IsApplicable(context2.get(), fact_manager)); + add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); } TEST(TransformationAddFunctionTest, diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp index eda6828f2b..7fb4fa0860 100644 --- a/test/fuzz/transformation_add_global_variable_test.cpp +++ b/test/fuzz/transformation_add_global_variable_test.cpp @@ -60,71 +60,78 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { FactManager fact_manager; // Id already in use - ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0).IsApplicable( - context.get(), fact_manager)); + ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0, true) + .IsApplicable(context.get(), fact_manager)); // %1 is not a type - ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0).IsApplicable( - context.get(), fact_manager)); + ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0, false) + .IsApplicable(context.get(), fact_manager)); // %7 is not a pointer type - ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0).IsApplicable( - context.get(), fact_manager)); + ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0, true) + .IsApplicable(context.get(), fact_manager)); // %9 does not have Private storage class - ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0).IsApplicable( - context.get(), fact_manager)); + ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0, false) + .IsApplicable(context.get(), fact_manager)); // %15 does not have Private storage class - ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0) + ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0, true) .IsApplicable(context.get(), fact_manager)); // %10 is a pointer to float, while %16 is an int constant - ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16) + ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16, false) .IsApplicable(context.get(), fact_manager)); // %10 is a Private pointer to float, while %15 is a variable with type // Uniform float pointer - ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15) + ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15, true) .IsApplicable(context.get(), fact_manager)); // %12 is a Private pointer to int, while %10 is a variable with type // Private float pointer - ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10) + ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10, false) .IsApplicable(context.get(), fact_manager)); // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK // since the initializer's type should be the *pointee* type. - ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14) + ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14, true) .IsApplicable(context.get(), fact_manager)); // This would work in principle, but logical addressing does not allow // a pointer to a pointer. - ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14) + ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14, false) .IsApplicable(context.get(), fact_manager)); TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, 0), + TransformationAddGlobalVariable(100, 12, 0, true), // %101 = OpVariable %10 Private - TransformationAddGlobalVariable(101, 10, 0), + TransformationAddGlobalVariable(101, 10, 0, false), // %102 = OpVariable %13 Private - TransformationAddGlobalVariable(102, 13, 0), + TransformationAddGlobalVariable(102, 13, 0, true), // %103 = OpVariable %12 Private %16 - TransformationAddGlobalVariable(103, 12, 16), + TransformationAddGlobalVariable(103, 12, 16, false), // %104 = OpVariable %19 Private %21 - TransformationAddGlobalVariable(104, 19, 21), + TransformationAddGlobalVariable(104, 19, 21, true), // %105 = OpVariable %19 Private %22 - TransformationAddGlobalVariable(105, 19, 22)}; + TransformationAddGlobalVariable(105, 19, 22, false)}; for (auto& transformation : transformations) { ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); transformation.Apply(context.get(), &fact_manager); } + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(100)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(102)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(104)); + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(101)); + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(103)); + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(105)); + ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -215,18 +222,21 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, 0), + TransformationAddGlobalVariable(100, 12, 0, true), // %101 = OpVariable %12 Private %16 - TransformationAddGlobalVariable(101, 12, 16), + TransformationAddGlobalVariable(101, 12, 16, false), // %102 = OpVariable %19 Private %21 - TransformationAddGlobalVariable(102, 19, 21)}; + TransformationAddGlobalVariable(102, 19, 21, true)}; for (auto& transformation : transformations) { ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); transformation.Apply(context.get(), &fact_manager); } + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(100)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(102)); + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(101)); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index ff3d91314d..7313538b09 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -1826,6 +1826,495 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationOutlineFunctionTest, OutlineLivesafe) { + // In the following, %30 is a livesafe function, with arbitrary parameter + // %200 and arbitrary local variable %201. Variable %100 is a loop limiter, + // which is not arbitrary. The test checks that the outlined function is + // livesafe, and that the parameters corresponding to %200 and %201 have the + // arbitrary fact associated with 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %199 = OpTypeFunction %2 %7 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %199 + %200 = OpFunctionParameter %7 + %31 = OpLabel + %100 = OpVariable %7 Function %8 + %201 = OpVariable %7 Function %8 + OpBranch %198 + %198 = OpLabel + OpBranch %20 + %20 = OpLabel + %101 = OpLoad %6 %100 + %102 = OpIAdd %6 %101 %9 + %202 = OpLoad %6 %200 + OpStore %201 %202 + OpStore %100 %102 + %103 = OpUGreaterThanEqual %11 %101 %10 + OpLoopMerge %21 %22 None + OpBranchConditional %103 %21 %104 + %104 = OpLabel + OpBranchConditional %12 %23 %21 + %23 = OpLabel + %105 = OpLoad %6 %100 + %106 = OpIAdd %6 %105 %9 + OpStore %100 %106 + %107 = OpUGreaterThanEqual %11 %105 %10 + OpLoopMerge %25 %26 None + OpBranchConditional %107 %25 %108 + %108 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + %109 = OpLoad %6 %100 + %110 = OpIAdd %6 %109 %9 + OpStore %100 %110 + %111 = OpUGreaterThanEqual %11 %109 %10 + OpLoopMerge %24 %27 None + OpBranchConditional %111 %24 %112 + %112 = OpLabel + OpBranchConditional %12 %24 %27 + %27 = OpLabel + OpBranch %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + OpBranch %197 + %197 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + fact_manager.AddFactFunctionIsLivesafe(30); + fact_manager.AddFactValueOfVariableIsArbitrary(200); + fact_manager.AddFactValueOfVariableIsArbitrary(201); + + TransformationOutlineFunction transformation( + /*entry_block*/ 198, + /*exit_block*/ 197, + /*new_function_struct_return_type_id*/ 400, + /*new_function_type_id*/ 401, + /*new_function_id*/ 402, + /*new_function_region_entry_block*/ 404, + /*new_caller_result_id*/ 405, + /*new_callee_result_id*/ 406, + /*input_id_to_fresh_id*/ {{100, 407}, {200, 408}, {201, 409}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // The original function should still be livesafe. + ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30)); + // The outlined function should be livesafe. + ASSERT_TRUE(fact_manager.FunctionIsLivesafe(402)); + // The variable and parameter that were originally arbitrary should still be. + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(200)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(201)); + // The loop limiter should still be non-arbitrary. + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(100)); + // The parameters for the original arbitrary variables should be arbitrary. + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(408)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(409)); + // The parameter for the loop limiter should not be arbitrary. + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(407)); + + 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %199 = OpTypeFunction %2 %7 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %401 = OpTypeFunction %2 %7 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %199 + %200 = OpFunctionParameter %7 + %31 = OpLabel + %100 = OpVariable %7 Function %8 + %201 = OpVariable %7 Function %8 + OpBranch %198 + %198 = OpLabel + %405 = OpFunctionCall %2 %402 %200 %100 %201 + OpReturn + OpFunctionEnd + %402 = OpFunction %2 None %401 + %408 = OpFunctionParameter %7 + %407 = OpFunctionParameter %7 + %409 = OpFunctionParameter %7 + %404 = OpLabel + OpBranch %20 + %20 = OpLabel + %101 = OpLoad %6 %407 + %102 = OpIAdd %6 %101 %9 + %202 = OpLoad %6 %408 + OpStore %409 %202 + OpStore %407 %102 + %103 = OpUGreaterThanEqual %11 %101 %10 + OpLoopMerge %21 %22 None + OpBranchConditional %103 %21 %104 + %104 = OpLabel + OpBranchConditional %12 %23 %21 + %23 = OpLabel + %105 = OpLoad %6 %407 + %106 = OpIAdd %6 %105 %9 + OpStore %407 %106 + %107 = OpUGreaterThanEqual %11 %105 %10 + OpLoopMerge %25 %26 None + OpBranchConditional %107 %25 %108 + %108 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + %109 = OpLoad %6 %407 + %110 = OpIAdd %6 %109 %9 + OpStore %407 %110 + %111 = OpUGreaterThanEqual %11 %109 %10 + OpLoopMerge %24 %27 None + OpBranchConditional %111 %24 %112 + %112 = OpLabel + OpBranchConditional %12 %24 %27 + %27 = OpLabel + OpBranch %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + OpBranch %197 + %197 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks1) { + // This checks that if all blocks in the region being outlined were dead, all + // blocks in the outlined function will be dead. + 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 %10 "foo(i1;" + OpName %9 "x" + OpName %12 "y" + OpName %21 "i" + OpName %46 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %15 = OpConstantFalse %14 + %22 = OpConstant %6 0 + %29 = OpConstant %6 10 + %41 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %46 = OpVariable %7 Function + OpStore %46 %13 + %47 = OpFunctionCall %2 %10 %46 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %21 = OpVariable %7 Function + OpStore %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + %18 = OpLoad %6 %9 + OpStore %12 %18 + %19 = OpLoad %6 %9 + %20 = OpIAdd %6 %19 %13 + OpStore %9 %20 + OpStore %21 %22 + OpBranch %23 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %28 = OpLoad %6 %21 + %30 = OpSLessThan %14 %28 %29 + OpBranchConditional %30 %24 %25 + %24 = OpLabel + %31 = OpLoad %6 %9 + %32 = OpLoad %6 %21 + %33 = OpSGreaterThan %14 %31 %32 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + OpBranch %26 + %35 = OpLabel + %37 = OpLoad %6 %9 + %38 = OpLoad %6 %12 + %39 = OpIAdd %6 %38 %37 + OpStore %12 %39 + OpBranch %26 + %26 = OpLabel + %40 = OpLoad %6 %21 + %42 = OpIAdd %6 %40 %41 + OpStore %21 %42 + OpBranch %23 + %25 = OpLabel + OpBranch %50 + %50 = OpLabel + OpBranch %17 + %17 = OpLabel + %43 = OpLoad %6 %9 + %44 = OpLoad %6 %12 + %45 = OpIAdd %6 %44 %43 + OpStore %12 %45 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u}) { + fact_manager.AddFactBlockIsDead(block_id); + } + + TransformationOutlineFunction transformation( + /*entry_block*/ 16, + /*exit_block*/ 50, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{9, 206}, {12, 207}, {21, 208}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + // All the original blocks, plus the new function entry block, should be dead. + for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u, 203u}) { + ASSERT_TRUE(fact_manager.BlockIsDead(block_id)); + } +} + +TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks2) { + // This checks that if some, but not all, blocks in the outlined region are + // dead, those (but not others) will be dead in the outlined function. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstantFalse %6 + %10 = OpTypePointer Function %6 + %12 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpBranch %30 + %30 = OpLabel + OpStore %8 %9 + OpBranch %31 + %31 = OpLabel + OpStore %11 %12 + OpSelectionMerge %36 None + OpBranchConditional %9 %32 %33 + %32 = OpLabel + OpBranch %34 + %33 = OpLabel + OpBranch %36 + %34 = OpLabel + OpBranch %35 + %35 = OpLabel + OpBranch %36 + %36 = OpLabel + OpBranch %37 + %37 = OpLabel + %13 = OpLoad %6 %8 + OpStore %11 %13 + %14 = OpLoad %6 %11 + OpStore %8 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + for (uint32_t block_id : {32u, 34u, 35u}) { + fact_manager.AddFactBlockIsDead(block_id); + } + + TransformationOutlineFunction transformation( + /*entry_block*/ 30, + /*exit_block*/ 37, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{11, 206}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + // The blocks that were originally dead, but not others, should be dead. + for (uint32_t block_id : {32u, 34u, 35u}) { + ASSERT_TRUE(fact_manager.BlockIsDead(block_id)); + } + for (uint32_t block_id : {5u, 30u, 31u, 33u, 36u, 37u, 203u}) { + ASSERT_FALSE(fact_manager.BlockIsDead(block_id)); + } +} + +TEST(TransformationOutlineFunctionTest, + OutlineWithArbitraryVariablesAndParameters) { + // This checks that if the outlined region uses a mixture of arbitrary and + // non-arbitrary variables and parameters, these properties are preserved + // during outlining. + 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 %7 + %13 = OpConstant %6 2 + %15 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %11 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %12 = OpLabel + %14 = OpVariable %7 Function + %20 = OpVariable %7 Function + OpBranch %50 + %50 = OpLabel + OpStore %9 %13 + OpStore %14 %15 + %16 = OpLoad %6 %14 + OpStore %10 %16 + %17 = OpLoad %6 %9 + %18 = OpLoad %6 %10 + %19 = OpIAdd %6 %17 %18 + OpStore %14 %19 + %21 = OpLoad %6 %9 + OpStore %20 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + fact_manager.AddFactValueOfVariableIsArbitrary(9); + fact_manager.AddFactValueOfVariableIsArbitrary(14); + + TransformationOutlineFunction transformation( + /*entry_block*/ 50, + /*exit_block*/ 50, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{9, 206}, {10, 207}, {14, 208}, {20, 209}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + // The variables that were originally abitrary, plus input parameters + // corresponding to them, should be arbitrary. The rest should not be. + for (uint32_t variable_id : {9u, 14u, 206u, 208u}) { + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(variable_id)); + } + for (uint32_t variable_id : {10u, 20u, 207u, 209u}) { + ASSERT_FALSE(fact_manager.BlockIsDead(variable_id)); + } +} + TEST(TransformationOutlineFunctionTest, Miscellaneous1) { // This tests outlining of some non-trivial code. From 7a2d408dea60aa1f8d53e123b01277c41b8f83b9 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 30 Jan 2020 20:01:05 +0000 Subject: [PATCH 04/88] Fix typo in comment. (#3163) --- source/opt/basic_block.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h index 0bab337147..6741a50f26 100644 --- a/source/opt/basic_block.h +++ b/source/opt/basic_block.h @@ -214,7 +214,7 @@ class BasicBlock { void KillAllInsts(bool killLabel); // Splits this basic block into two. Returns a new basic block with label - // |labelId| containing the instructions from |iter| onwards. Instructions + // |label_id| containing the instructions from |iter| onwards. Instructions // prior to |iter| remain in this basic block. The new block will be added // to the function immediately after the original block. BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id, From 0265a9d4de03e6c0fa58e0e56ac798077110e987 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 3 Feb 2020 06:20:47 -0800 Subject: [PATCH 05/88] Implement constant folding for many transcendentals (#3166) * Implement constant folding for many transcendentals This change adds support for folding of sin/cos/tan/asin/acos/atan, exp/log/exp2/log2, sqrt, atan2 and pow. The mechanism allows to use any C function to implement folding in the future; for now I limited the actual additions to the most commonly used intrinsics in the shaders. Unary folder had to be tweaked to work with extended instructions - for extended instructions, constants.size() == 2 and constants[0] == nullptr. This adjustment is similar to the one binary folder already performs. Fixes #1390. * Fix Android build On old versions of Android NDK, we don't get std::exp2/std::log2 because of partial C++11 support. We do get ::exp2, but not ::log2 so we need to emulate that. --- source/opt/const_folding_rules.cpp | 102 ++++++++++++++++++++- test/opt/fold_test.cpp | 140 +++++++++++++++++++++++++++-- 2 files changed, 230 insertions(+), 12 deletions(-) diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index 2a2493fd6f..d262a7ecab 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp @@ -265,7 +265,10 @@ ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) { return nullptr; } - if (constants[0] == nullptr) { + const analysis::Constant* arg = + (inst->opcode() == SpvOpExtInst) ? constants[1] : constants[0]; + + if (arg == nullptr) { return nullptr; } @@ -273,7 +276,7 @@ ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) { std::vector a_components; std::vector results_components; - a_components = constants[0]->GetVectorComponents(const_mgr); + a_components = arg->GetVectorComponents(const_mgr); // Fold each component of the vector. for (uint32_t i = 0; i < a_components.size(); ++i) { @@ -291,7 +294,7 @@ ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) { } return const_mgr->GetConstant(vector_type, ids); } else { - return scalar_rule(result_type, constants[0], const_mgr); + return scalar_rule(result_type, arg, const_mgr); } }; } @@ -1070,6 +1073,60 @@ const analysis::Constant* FoldClamp3( return nullptr; } +UnaryScalarFoldingRule FoldFTranscendentalUnary(double (*fp)(double)) { + return + [fp](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(float_type == result_type->AsFloat()); + if (float_type->width() == 32) { + float fa = a->GetFloat(); + float res = static_cast(fp(fa)); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + double res = fp(fa); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +BinaryScalarFoldingRule FoldFTranscendentalBinary(double (*fp)(double, + double)) { + return + [fp](const analysis::Type* result_type, const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(float_type == result_type->AsFloat()); + assert(float_type == b->type()->AsFloat()); + if (float_type->width() == 32) { + float fa = a->GetFloat(); + float fb = b->GetFloat(); + float res = static_cast(fp(fa, fb)); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + double fb = b->GetDouble(); + double res = fp(fa, fb); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} } // namespace void ConstantFoldingRules::AddFoldingRules() { @@ -1175,6 +1232,45 @@ void ConstantFoldingRules::AddFoldingRules() { FoldClamp2); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sin}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::sin))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Cos}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::cos))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Tan}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::tan))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Asin}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::asin))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Acos}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::acos))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::atan))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::log))); + +#ifdef __ANDROID__ + // Android NDK r15c tageting ABI 15 doesn't have full support for C++11 + // (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't + // available up until ABI 18 so we use a shim + auto log2_shim = [](double v) -> double { return log(v) / log(2.0); }; + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(::exp2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(log2_shim))); +#else + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::log2))); +#endif + + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sqrt}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::sqrt))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan2}].push_back( + FoldFPBinaryOp(FoldFTranscendentalBinary(std::atan2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Pow}].push_back( + FoldFPBinaryOp(FoldFTranscendentalBinary(std::pow))); } } } // namespace opt diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 26d12201a3..db0192450e 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -1693,7 +1693,7 @@ ::testing::Values( "OpReturn\n" + "OpFunctionEnd", 2, 0.2f), - // Test case 21: FMax 1.0 4.0 + // Test case 23: FMax 1.0 4.0 InstructionFoldingCase( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1701,7 +1701,7 @@ ::testing::Values( "OpReturn\n" + "OpFunctionEnd", 2, 4.0f), - // Test case 22: FMax 1.0 0.2 + // Test case 24: FMax 1.0 0.2 InstructionFoldingCase( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1709,7 +1709,7 @@ ::testing::Values( "OpReturn\n" + "OpFunctionEnd", 2, 1.0f), - // Test case 23: FClamp 1.0 0.2 4.0 + // Test case 25: FClamp 1.0 0.2 4.0 InstructionFoldingCase( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1717,7 +1717,7 @@ ::testing::Values( "OpReturn\n" + "OpFunctionEnd", 2, 1.0f), - // Test case 24: FClamp 0.2 2.0 4.0 + // Test case 26: FClamp 0.2 2.0 4.0 InstructionFoldingCase( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1725,7 +1725,7 @@ ::testing::Values( "OpReturn\n" + "OpFunctionEnd", 2, 2.0f), - // Test case 25: FClamp 2049.0 2.0 4.0 + // Test case 27: FClamp 2049.0 2.0 4.0 InstructionFoldingCase( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1733,7 +1733,7 @@ ::testing::Values( "OpReturn\n" + "OpFunctionEnd", 2, 4.0f), - // Test case 26: FClamp 1.0 2.0 x + // Test case 28: FClamp 1.0 2.0 x InstructionFoldingCase( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1742,7 +1742,7 @@ ::testing::Values( "OpReturn\n" + "OpFunctionEnd", 2, 2.0), - // Test case 27: FClamp 1.0 x 0.5 + // Test case 29: FClamp 1.0 x 0.5 InstructionFoldingCase( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1750,7 +1750,111 @@ ::testing::Values( "%2 = OpExtInst %float %1 FClamp %float_1 %undef %float_0p5\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 0.5) + 2, 0.5), + // Test case 30: Sin 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Sin %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 31: Cos 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Cos %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 32: Tan 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Tan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 33: Asin 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Asin %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 34: Acos 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Acos %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 35: Atan 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Atan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 36: Exp 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Exp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 37: Log 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Log %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 38: Exp2 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Exp2 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 39: Log2 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Log2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 40: Sqrt 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Sqrt %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 41: Atan2 0.0 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Atan2 %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 42: Pow 2.0 3.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Pow %float_2 %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 8.0) )); // clang-format on @@ -1967,7 +2071,25 @@ ::testing::Values( "%2 = OpExtInst %double %1 FClamp %double_1 %undef %double_0p5\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 0.5) + 2, 0.5), + // Test case 21: Sqrt 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 Sqrt %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 22: Pow 2.0 3.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 Pow %double_2 %double_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 8.0) )); // clang-format on From ddcc11763a38485bb0fd16e1bdbfe6276e6eac01 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 3 Feb 2020 09:47:28 -0500 Subject: [PATCH 06/88] Update CHANGES --- CHANGES | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 2f2968e2b7..be36c1754d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,27 @@ Revision history for SPIRV-Tools v2020.1-dev 2019-12-11 - - Start v2020.1-dev + - General: + - Add support for SPV_KHR_non_semantic_info (#3110) + - Support OpenCL.DebugInfo.100 extended instruction set (#3080) + - Added support for Vulkan 1.2 + - Add API function to better handle getting the necessary environment (#3142) + - Clarify mapping of target env to SPIR-V version (#3150) + - Implement constant folding for many transcendentals (#3166) + - Optimizer + - Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119) + - Better handling of OpLine on merge blocks (#3130) + - Use dummy switch instead of dummy loop in MergeReturn pass. (#3151) + - Validator + - Fix structured exit validation (#3141) + - Reduce + - Fuzz + - Fuzzer pass to merge blocks (#3097) + - Transformation to add a new function to a module (#3114) + - Add fuzzer pass to perform module donation (#3117) + - Fuzzer passes to create and branch to new dead blocks (#3135) + - Linker: + - Remove names and decorations of imported symbols (#3081) v2019.5 2019-12-11 - General: From a9624b4d5d0bb1617d6e20dbaba174b07cee18ee Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Mon, 3 Feb 2020 12:13:32 -0500 Subject: [PATCH 07/88] Handle TimeAMD in AmdExtensionToKhrPass. (#3168) This adds support for replacing TimeAMD with OpReadClockKHR. The scope for OpReadClockKHR is fixed to be a subgroup because TimeAMD operates only on subgroup. --- source/opt/amd_ext_to_khr.cpp | 45 +++++++++++++++++++++++++++-------- test/opt/amd_ext_to_khr.cpp | 37 +++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp index e9b7f8613e..ccedc0bc5c 100644 --- a/source/opt/amd_ext_to_khr.cpp +++ b/source/opt/amd_ext_to_khr.cpp @@ -53,12 +53,6 @@ analysis::Type* GetUIntType(IRContext* ctx) { return ctx->get_type_mgr()->GetRegisteredType(&int_type); } -bool NotImplementedYet(IRContext*, Instruction*, - const std::vector&) { - assert(false && "Not implemented."); - return false; -} - // Returns a folding rule that replaces |op(a,b,c)| by |op(op(a,b),c)|, where // |op| is either min or max. |opcode| is the binary opcode in the GLSLstd450 // extended instruction set that corresponds to the trinary instruction being @@ -686,13 +680,13 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst, return true; } -// A folding rule that will replace the CubeFaceCoordAMD extended +// A folding rule that will replace the CubeFaceIndexAMD extended // instruction in the SPV_AMD_gcn_shader_ballot. Returns true if the folding // is successful. // // The instruction // -// %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input +// %result = OpExtInst %float %1 CubeFaceIndexAMD %input // // with // @@ -705,7 +699,7 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst, // %is_z_neg = OpFOrdLessThan %bool %z %float_0 // %is_y_neg = OpFOrdLessThan %bool %y %float_0 // %is_x_neg = OpFOrdLessThan %bool %x %float_0 -// %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax +// %amax_x_y = OpExtInst %float %n_1 FMax %ax %ay // %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y // %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax // %case_z = OpSelect %float %is_z_neg %float_5 %float4 @@ -800,6 +794,37 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst, return true; } +// A folding rule that will replace the TimeAMD extended instruction in the +// SPV_AMD_gcn_shader_ballot. It returns true if the folding is successful. +// It returns False, otherwise. +// +// The instruction +// +// %result = OpExtInst %uint64 %1 TimeAMD +// +// with +// +// %result = OpReadClockKHR %uint64 %uint_3 +// +// NOTE: TimeAMD uses subgroup scope (it is not a real time clock). +bool ReplaceTimeAMD(IRContext* ctx, Instruction* inst, + const std::vector&) { + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + ctx->AddExtension("SPV_KHR_shader_clock"); + ctx->AddCapability(SpvCapabilityShaderClockKHR); + + inst->SetOpcode(SpvOpReadClockKHR); + Instruction::OperandList args; + uint32_t subgroup_scope_id = ir_builder.GetUintConstantId(SpvScopeSubgroup); + args.push_back({SPV_OPERAND_TYPE_ID, {subgroup_scope_id}}); + inst->SetInOperands(std::move(args)); + ctx->UpdateDefUse(inst); + + return true; +} + class AmdExtFoldingRules : public FoldingRules { public: explicit AmdExtFoldingRules(IRContext* ctx) : FoldingRules(ctx) {} @@ -869,7 +894,7 @@ class AmdExtFoldingRules : public FoldingRules { ReplaceCubeFaceCoord); ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back( ReplaceCubeFaceIndex); - ext_rules_[{extension_id, TimeAMD}].push_back(NotImplementedYet); + ext_rules_[{extension_id, TimeAMD}].push_back(ReplaceTimeAMD); } } }; diff --git a/test/opt/amd_ext_to_khr.cpp b/test/opt/amd_ext_to_khr.cpp index d943d34256..3340e898ce 100644 --- a/test/opt/amd_ext_to_khr.cpp +++ b/test/opt/amd_ext_to_khr.cpp @@ -15,7 +15,6 @@ #include #include "gmock/gmock.h" - #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" @@ -913,6 +912,42 @@ TEST_F(AmdExtToKhrTest, SetVersion1) { EXPECT_THAT(output, HasSubstr("Version: 1.4")); } +TEST_F(AmdExtToKhrTest, TimeAMD) { + const std::string text = R"( + OpCapability Shader + OpCapability Int64 + OpExtension "SPV_AMD_gcn_shader" +; CHECK-NOT: OpExtension "SPV_AMD_gcn_shader" +; CHECK: OpExtension "SPV_KHR_shader_clock" + %1 = OpExtInstImport "GLSL.std.450" + %2 = OpExtInstImport "SPV_AMD_gcn_shader" +; CHECK-NOT: OpExtInstImport "SPV_AMD_gcn_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_AMD_gcn_shader" + OpSourceExtension "GL_ARB_gpu_shader_int64" + OpName %main "main" + OpName %time "time" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong + %main = OpFunction %void None %6 + %9 = OpLabel + %time = OpVariable %_ptr_Function_ulong Function +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_3:%\w+]] = OpConstant [[uint]] 3 + %10 = OpExtInst %ulong %2 TimeAMD +; CHECK: %10 = OpReadClockKHR %ulong [[uint_3]] + OpStore %time %10 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} } // namespace } // namespace opt } // namespace spvtools From b7e0998e3d9cecfb77c7dd4b2e52e11724fd275a Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 4 Feb 2020 11:15:07 +0000 Subject: [PATCH 08/88] spirv-fuzz: Disallow copying of null and undefined pointers (#3172) If the fuzzer object-copies a pointer we would like to be able to perform loads from the copy (and stores to it, if its value is known not to matter). Undefined and null pointers present a problem here, so this change disallows copying them. --- source/fuzz/fuzzer_util.cpp | 14 +++++++ source/fuzz/transformation_copy_object.h | 2 + test/fuzz/transformation_copy_object_test.cpp | 38 +++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index b2ace38f17..f9f9969cdd 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -226,6 +226,20 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) { // We can only make a synonym of an instruction that has a type. return false; } + auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id()); + if (type_inst->opcode() == SpvOpTypePointer) { + switch (inst->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + // We disallow making synonyms of null or undefined pointers. This is + // to provide the property that if the original shader exhibited no bad + // pointer accesses, the transformed shader will not either. + return false; + default: + break; + } + } + // 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. diff --git a/source/fuzz/transformation_copy_object.h b/source/fuzz/transformation_copy_object.h index 3a75ac9d9b..ac5e978df6 100644 --- a/source/fuzz/transformation_copy_object.h +++ b/source/fuzz/transformation_copy_object.h @@ -47,6 +47,8 @@ class TransformationCopyObject : public Transformation { // - It must be legal to insert an OpCopyObject instruction directly // before 'inst'. // - |message_.object| must be available directly before 'inst'. + // - |message_.object| must not be a null pointer or undefined pointer (so as + // to make it legal to load from copied pointers). bool IsApplicable(opt::IRContext* context, const FactManager& fact_manager) const override; diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp index b489f71593..a33f58de3e 100644 --- a/test/fuzz/transformation_copy_object_test.cpp +++ b/test/fuzz/transformation_copy_object_test.cpp @@ -588,6 +588,44 @@ TEST(TransformationCopyObjectTest, MiscellaneousCopies) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationCopyObjectTest, DoNotCopyNullOrUndefPointers) { + 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpConstantNull %7 + %9 = OpUndef %7 + %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, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Illegal to copy null. + ASSERT_FALSE(TransformationCopyObject( + 8, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100) + .IsApplicable(context.get(), fact_manager)); + + // Illegal to copy an OpUndef of pointer type. + ASSERT_FALSE(TransformationCopyObject( + 9, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100) + .IsApplicable(context.get(), fact_manager)); +} + } // namespace } // namespace fuzz } // namespace spvtools From fdd0c87765943221c1518204674b10ff41722a9f Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 4 Feb 2020 13:57:47 +0000 Subject: [PATCH 09/88] Update script that checks copyright years. (#3173) --- utils/check_copyright.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/check_copyright.py b/utils/check_copyright.py index 969371de22..2d288a1226 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -32,9 +32,9 @@ 'Google LLC', 'Pierre Moreau', 'Samsung Inc'] -CURRENT_YEAR='2019' +CURRENT_YEAR='2020' -YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019)' +YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)' COPYRIGHT_RE = re.compile( 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS))) From bb56e892f58658007f86742b9848f146488a32d3 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 4 Feb 2020 14:00:19 +0000 Subject: [PATCH 10/88] spirv-fuzz: Fuzzer pass to add composite types (#3171) Adds a fuzzer pass that randomly adds vector and matrix types not already present in the module, and randomly adds structs with random field types and arrays with random base types and sizes. Other passes will be able to create variables and ids using these types. --- source/fuzz/CMakeLists.txt | 2 + source/fuzz/fuzzer.cpp | 4 + source/fuzz/fuzzer_context.cpp | 19 +++ source/fuzz/fuzzer_context.h | 25 ++++ source/fuzz/fuzzer_pass.cpp | 53 +++++++ source/fuzz/fuzzer_pass.h | 17 +++ .../fuzz/fuzzer_pass_add_composite_types.cpp | 138 ++++++++++++++++++ source/fuzz/fuzzer_pass_add_composite_types.h | 61 ++++++++ source/fuzz/transformation_add_type_matrix.h | 4 +- source/fuzz/transformation_add_type_vector.h | 4 +- 10 files changed, 323 insertions(+), 4 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_add_composite_types.cpp create mode 100644 source/fuzz/fuzzer_pass_add_composite_types.h diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index ea5e216fa7..c816f87576 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -36,6 +36,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer.h fuzzer_context.h fuzzer_pass.h + fuzzer_pass_add_composite_types.h fuzzer_pass_add_dead_blocks.h fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_dead_continues.h @@ -107,6 +108,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer.cpp fuzzer_context.cpp fuzzer_pass.cpp + fuzzer_pass_add_composite_types.cpp fuzzer_pass_add_dead_blocks.cpp fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_dead_continues.cpp diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index a624c11781..27b697c947 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -21,6 +21,7 @@ #include "fuzzer_pass_adjust_memory_operands_masks.h" #include "source/fuzz/fact_manager.h" #include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass_add_composite_types.h" #include "source/fuzz/fuzzer_pass_add_dead_blocks.h" #include "source/fuzz/fuzzer_pass_add_dead_breaks.h" #include "source/fuzz/fuzzer_pass_add_dead_continues.h" @@ -178,6 +179,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( // Apply some semantics-preserving passes. std::vector> passes; while (passes.empty()) { + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index afffcf54c8..916803a7bb 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -23,11 +23,16 @@ namespace { // Default pairs of probabilities for applying various // transformations. All values are percentages. Keep them in alphabetical order. +const std::pair kChanceOfAddingAnotherStructField = {20, + 90}; +const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; const std::pair kChanceOfAddingDeadBlock = {20, 90}; const std::pair kChanceOfAddingDeadBreak = {5, 80}; const std::pair kChanceOfAddingDeadContinue = {5, 80}; +const std::pair kChanceOfAddingMatrixType = {20, 70}; const std::pair kChanceOfAddingNoContractionDecoration = { 5, 70}; +const std::pair kChanceOfAddingVectorType = {20, 70}; const std::pair kChanceOfAdjustingFunctionControl = {20, 70}; const std::pair kChanceOfAdjustingLoopControl = {20, 90}; @@ -35,6 +40,8 @@ const std::pair kChanceOfAdjustingMemoryOperandsMask = {20, 90}; const std::pair kChanceOfAdjustingSelectionControl = {20, 90}; +const std::pair kChanceOfChoosingStructTypeVsArrayType = { + 20, 80}; const std::pair kChanceOfConstructingComposite = {20, 50}; const std::pair kChanceOfCopyingObject = {20, 50}; const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; @@ -51,6 +58,7 @@ const std::pair kChanceOfSplittingBlock = {40, 95}; const uint32_t kDefaultMaxLoopControlPartialCount = 100; const uint32_t kDefaultMaxLoopControlPeelCount = 100; const uint32_t kDefaultMaxLoopLimit = 20; +const uint32_t kDefaultMaxNewArraySizeLimit = 100; // Default functions for controlling how deep to go during recursive // generation/transformation. Keep them in alphabetical order. @@ -70,14 +78,22 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, next_fresh_id_(min_fresh_id), go_deeper_in_constant_obfuscation_( kDefaultGoDeeperInConstantObfuscation) { + chance_of_adding_another_struct_field_ = + ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); + chance_of_adding_array_or_struct_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); chance_of_adding_dead_block_ = ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock); chance_of_adding_dead_break_ = ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak); chance_of_adding_dead_continue_ = ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue); + chance_of_adding_matrix_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingMatrixType); chance_of_adding_no_contraction_decoration_ = ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration); + chance_of_adding_vector_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingVectorType); chance_of_adjusting_function_control_ = ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl); chance_of_adjusting_loop_control_ = @@ -86,6 +102,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask); chance_of_adjusting_selection_control_ = ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl); + chance_of_choosing_struct_type_vs_array_type_ = + ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType); chance_of_constructing_composite_ = ChooseBetweenMinAndMax(kChanceOfConstructingComposite); chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); @@ -106,6 +124,7 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount; max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount; max_loop_limit_ = kDefaultMaxLoopLimit; + max_new_array_size_limit_ = kDefaultMaxNewArraySizeLimit; } FuzzerContext::~FuzzerContext() = default; diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index cc4337093d..d4d6d58fbb 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -58,14 +58,26 @@ class FuzzerContext { // Probabilities associated with applying various transformations. // Keep them in alphabetical order. + uint32_t GetChanceOfAddingAnotherStructField() { + return chance_of_adding_another_struct_field_; + } + uint32_t GetChanceOfAddingArrayOrStructType() { + return chance_of_adding_array_or_struct_type_; + } uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; } uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; } uint32_t GetChanceOfAddingDeadContinue() { return chance_of_adding_dead_continue_; } + uint32_t GetChanceOfAddingMatrixType() { + return chance_of_adding_matrix_type_; + } uint32_t GetChanceOfAddingNoContractionDecoration() { return chance_of_adding_no_contraction_decoration_; } + uint32_t GetChanceOfAddingVectorType() { + return chance_of_adding_vector_type_; + } uint32_t GetChanceOfAdjustingFunctionControl() { return chance_of_adjusting_function_control_; } @@ -78,6 +90,9 @@ class FuzzerContext { uint32_t GetChanceOfAdjustingSelectionControl() { return chance_of_adjusting_selection_control_; } + uint32_t GetChanceOfChoosingStructTypeVsArrayType() { + return chance_of_choosing_struct_type_vs_array_type_; + } uint32_t GetChanceOfConstructingComposite() { return chance_of_constructing_composite_; } @@ -109,6 +124,10 @@ class FuzzerContext { uint32_t GetRandomLoopLimit() { return random_generator_->RandomUint32(max_loop_limit_); } + uint32_t GetRandomSizeForNewArray() { + // Ensure that the array size is non-zero. + return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; + } // Functions to control how deeply to recurse. // Keep them in alphabetical order. @@ -124,14 +143,19 @@ class FuzzerContext { // Probabilities associated with applying various transformations. // Keep them in alphabetical order. + uint32_t chance_of_adding_another_struct_field_; + uint32_t chance_of_adding_array_or_struct_type_; uint32_t chance_of_adding_dead_block_; uint32_t chance_of_adding_dead_break_; uint32_t chance_of_adding_dead_continue_; + uint32_t chance_of_adding_matrix_type_; uint32_t chance_of_adding_no_contraction_decoration_; + uint32_t chance_of_adding_vector_type_; uint32_t chance_of_adjusting_function_control_; uint32_t chance_of_adjusting_loop_control_; uint32_t chance_of_adjusting_memory_operands_mask_; uint32_t chance_of_adjusting_selection_control_; + uint32_t chance_of_choosing_struct_type_vs_array_type_; uint32_t chance_of_constructing_composite_; uint32_t chance_of_copying_object_; uint32_t chance_of_donating_additional_module_; @@ -149,6 +173,7 @@ class FuzzerContext { uint32_t max_loop_control_partial_count_; uint32_t max_loop_control_peel_count_; uint32_t max_loop_limit_; + uint32_t max_new_array_size_limit_; // Functions to determine with what probability to go deeper when generating // or mutating constructs recursively. diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 9d891a59d7..9964a6c3f5 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -18,8 +18,11 @@ #include "source/fuzz/transformation_add_constant_scalar.h" #include "source/fuzz/transformation_add_global_undef.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_matrix.h" #include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_vector.h" namespace spvtools { namespace fuzz { @@ -155,6 +158,56 @@ uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) { return result; } +uint32_t FuzzerPass::FindOrCreate32BitFloatType() { + opt::analysis::Float float_type(32); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeFloat(result, 32)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id, + uint32_t component_count) { + assert(component_count >= 2 && component_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + opt::analysis::Type* component_type = + GetIRContext()->get_type_mgr()->GetType(component_type_id); + assert(component_type && "Precondition: the component type must exist."); + opt::analysis::Vector vector_type(component_type, component_count); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeVector(result, component_type_id, component_count)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count, + uint32_t row_count) { + assert(column_count >= 2 && column_count <= 4 && + "Precondition: column count must be in range [2, 4]."); + assert(row_count >= 2 && row_count <= 4 && + "Precondition: row count must be in range [2, 4]."); + uint32_t column_type_id = + FindOrCreateVectorType(FindOrCreate32BitFloatType(), row_count); + opt::analysis::Type* column_type = + GetIRContext()->get_type_mgr()->GetType(column_type_id); + opt::analysis::Matrix matrix_type(column_type, column_count); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeMatrix(result, column_type_id, column_count)); + return result; +} + uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType( bool is_signed, SpvStorageClass storage_class) { auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed); diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 09f831f4b9..e1e8aec25c 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -108,6 +108,23 @@ class FuzzerPass { // transformation is applied to add it. uint32_t FindOrCreate32BitIntegerType(bool is_signed); + // Returns the id of an OpTypeFloat instruction, with width 32. If such an + // instruction does not exist, a transformation is applied to add it. + uint32_t FindOrCreate32BitFloatType(); + + // Returns the id of an OpTypeVector instruction, with |component_type_id| + // (which must already exist) as its base type, and |component_count| + // elements (which must be in the range [2, 4]). If such an instruction does + // not exist, a transformation is applied to add it. + uint32_t FindOrCreateVectorType(uint32_t component_type_id, + uint32_t component_count); + + // Returns the id of an OpTypeMatrix instruction, with |column_count| columns + // and |row_count| rows (each of which must be in the range [2, 4]). If the + // float and vector types required to build this matrix type or the matrix + // type itself do not exist, transformations are applied to add them. + uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count); + // Returns the id of an OpTypePointer instruction, with a 32-bit integer base // type of signedness specified by |is_signed|. If the pointer type or // required integer base type do not exist, transformations are applied to add diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp new file mode 100644 index 0000000000..32c720e16f --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_composite_types.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_type_array.h" +#include "source/fuzz/transformation_add_type_struct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default; + +void FuzzerPassAddCompositeTypes::Apply() { + MaybeAddMissingVectorTypes(); + MaybeAddMissingMatrixTypes(); + + // Randomly interleave between adding struct and array composite types + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingArrayOrStructType())) { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfChoosingStructTypeVsArrayType())) { + AddNewStructType(); + } else { + AddNewArrayType(); + } + } +} + +void FuzzerPassAddCompositeTypes::MaybeAddMissingVectorTypes() { + // Functions to lazily supply scalar base types on demand if we decide to + // create vectors with the relevant base types. + std::function bool_type_supplier = [this]() -> uint32_t { + return FindOrCreateBoolType(); + }; + std::function float_type_supplier = [this]() -> uint32_t { + return FindOrCreate32BitFloatType(); + }; + std::function int_type_supplier = [this]() -> uint32_t { + return FindOrCreate32BitIntegerType(true); + }; + std::function uint_type_supplier = [this]() -> uint32_t { + return FindOrCreate32BitIntegerType(false); + }; + + // Consider each of the base types with which we can make vectors. + for (auto& base_type_supplier : {bool_type_supplier, float_type_supplier, + int_type_supplier, uint_type_supplier}) { + // Consider each valid vector size. + for (uint32_t size = 2; size <= 4; size++) { + // Randomly decide whether to create (if it does not already exist) a + // vector with this size and base type. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingVectorType())) { + FindOrCreateVectorType(base_type_supplier(), size); + } + } + } +} + +void FuzzerPassAddCompositeTypes::MaybeAddMissingMatrixTypes() { + // Consider every valid matrix dimension. + for (uint32_t columns = 2; columns <= 4; columns++) { + for (uint32_t rows = 2; rows <= 4; rows++) { + // Randomly decide whether to create (if it does not already exist) a + // matrix with these dimensions. As matrices can only have floating-point + // base type, we do not need to consider multiple base types as in the + // case for vectors. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingMatrixType())) { + FindOrCreateMatrixType(columns, rows); + } + } + } +} + +void FuzzerPassAddCompositeTypes::AddNewArrayType() { + ApplyTransformation(TransformationAddTypeArray( + GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(), + FindOrCreate32BitIntegerConstant( + GetFuzzerContext()->GetRandomSizeForNewArray(), false))); +} + +void FuzzerPassAddCompositeTypes::AddNewStructType() { + std::vector field_type_ids; + do { + field_type_ids.push_back(ChooseScalarOrCompositeType()); + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAnotherStructField())); + ApplyTransformation(TransformationAddTypeStruct( + GetFuzzerContext()->GetFreshId(), field_type_ids)); +} + +uint32_t FuzzerPassAddCompositeTypes::ChooseScalarOrCompositeType() { + // Gather up all the possibly-relevant types. + std::vector candidates; + for (auto& inst : GetIRContext()->types_values()) { + switch (inst.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeStruct: + case SpvOpTypeVector: + candidates.push_back(inst.result_id()); + break; + default: + break; + } + } + assert(!candidates.empty() && + "This function should only be called if there is at least one scalar " + "or composite type available."); + // Return one of these types at random. + return candidates[GetFuzzerContext()->RandomIndex(candidates)]; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h new file mode 100644 index 0000000000..29d4bb896f --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_composite_types.h @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds missing vector and matrix types, and new +// array and struct types, to the module. +class FuzzerPassAddCompositeTypes : public FuzzerPass { + public: + FuzzerPassAddCompositeTypes( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddCompositeTypes(); + + void Apply() override; + + private: + // Creates an array of a random size with a random existing base type and adds + // it to the module. + void AddNewArrayType(); + + // Creates a struct with fields of random existing types and adds it to the + // module. + void AddNewStructType(); + + // For each vector type not already present in the module, randomly decides + // whether to add it to the module. + void MaybeAddMissingVectorTypes(); + + // For each matrix type not already present in the module, randomly decides + // whether to add it to the module. + void MaybeAddMissingMatrixTypes(); + + // Returns the id of a scalar or composite type declared in the module, + // chosen randomly. + uint32_t ChooseScalarOrCompositeType(); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ diff --git a/source/fuzz/transformation_add_type_matrix.h b/source/fuzz/transformation_add_type_matrix.h index ee3caf7c08..69d638906c 100644 --- a/source/fuzz/transformation_add_type_matrix.h +++ b/source/fuzz/transformation_add_type_matrix.h @@ -28,8 +28,8 @@ class TransformationAddTypeMatrix : public Transformation { explicit TransformationAddTypeMatrix( const protobufs::TransformationAddTypeMatrix& message); - TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t base_type_id, - uint32_t size); + TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t column_type_id, + uint32_t column_count); // - |message_.fresh_id| must be a fresh id // - |message_.column_type_id| must be the id of a floating-point vector type diff --git a/source/fuzz/transformation_add_type_vector.h b/source/fuzz/transformation_add_type_vector.h index 7b50f6a388..af840f5ebe 100644 --- a/source/fuzz/transformation_add_type_vector.h +++ b/source/fuzz/transformation_add_type_vector.h @@ -28,8 +28,8 @@ class TransformationAddTypeVector : public Transformation { explicit TransformationAddTypeVector( const protobufs::TransformationAddTypeVector& message); - TransformationAddTypeVector(uint32_t fresh_id, uint32_t base_type_id, - uint32_t size); + TransformationAddTypeVector(uint32_t fresh_id, uint32_t component_type_id, + uint32_t component_count); // - |message_.fresh_id| must be a fresh id // - |message_.component_type_id| must be the id of a scalar type From 76616bab663e24629147ea55fb30b0013a758095 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 4 Feb 2020 09:33:50 -0500 Subject: [PATCH 11/88] Update CHANGES --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index be36c1754d..b8c8dda257 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ v2020.1-dev 2019-12-11 - Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119) - Better handling of OpLine on merge blocks (#3130) - Use dummy switch instead of dummy loop in MergeReturn pass. (#3151) + - Handle TimeAMD in AmdExtensionToKhrPass. (#3168) - Validator - Fix structured exit validation (#3141) - Reduce @@ -20,6 +21,7 @@ v2020.1-dev 2019-12-11 - Transformation to add a new function to a module (#3114) - Add fuzzer pass to perform module donation (#3117) - Fuzzer passes to create and branch to new dead blocks (#3135) + - Fuzzer pass to add composite types (#3171) - Linker: - Remove names and decorations of imported symbols (#3081) From 8dd174809fb86498e5870276be7c84a8bf4c8254 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 4 Feb 2020 09:35:13 -0500 Subject: [PATCH 12/88] Finalize SPIRV-Tools v2020.1 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index b8c8dda257..4130e9652b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ Revision history for SPIRV-Tools -v2020.1-dev 2019-12-11 +v2020.1 2020-02-03 - General: - Add support for SPV_KHR_non_semantic_info (#3110) - Support OpenCL.DebugInfo.100 extended instruction set (#3080) From da5457f6b771f19707e9291ebd54737ad50bfa60 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 4 Feb 2020 09:36:21 -0500 Subject: [PATCH 13/88] Start SPIRV-Tools v2020.2 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 4130e9652b..48c93a4d60 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ Revision history for SPIRV-Tools +v2020.2-dev 2020-02-03 + - Start v2020.2-dev + v2020.1 2020-02-03 - General: - Add support for SPV_KHR_non_semantic_info (#3110) From a3b5ad909ebe99c4436920a252c44fde28c11181 Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Tue, 4 Feb 2020 11:13:24 -0500 Subject: [PATCH 14/88] Fix typos in opt's help. Update environment version. (#3170) --- tools/opt/opt.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 0ff937ae56..658bd5bdce 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -111,7 +111,7 @@ Options (in lexicographical order):)", printf(R"( --amd-ext-to-khr Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, - and VK_AMD_shader_trinary_minmax with equivalant code using core + and VK_AMD_shader_trinary_minmax with equivalent code using core instructions and capabilities.)"); printf(R"( --ccp @@ -173,7 +173,7 @@ Options (in lexicographical order):)", printf(R"( --eliminate-dead-branches Convert conditional branches with constant condition to the - indicated unconditional brranch. Delete all resulting dead + indicated unconditional branch. Delete all resulting dead code. Performed only on entry point call tree functions.)"); printf(R"( --eliminate-dead-code-aggressive @@ -302,7 +302,7 @@ Options (in lexicographical order):)", the optimization is above the threshold.)"); printf(R"( --max-id-bound= - Sets the maximum value for the id bound for the moudle. The + Sets the maximum value for the id bound for the module. The default is the minimum value for this limit, 0x3FFFFF. See section 2.17 of the Spir-V specification.)"); printf(R"( @@ -428,7 +428,7 @@ Options (in lexicographical order):)", --scalar-replacement[=] Replace aggregate function scope variables that are only accessed via their elements with new function variables representing each - element. is a limit on the size of the aggragates that will + element. is a limit on the size of the aggregates that will be replaced. 0 means there is no limit. The default value is 100.)"); printf(R"( @@ -447,7 +447,7 @@ Options (in lexicographical order):)", --split-invalid-unreachable Attempts to legalize for WebGPU cases where an unreachable merge-block is also a continue-target by splitting it into two - seperate blocks. There exist legal, for Vulkan, instances of this + separate blocks. There exist legal, for Vulkan, instances of this pattern that cannot be converted into legal WebGPU, so this conversion may not succeed.)"); printf(R"( @@ -472,7 +472,7 @@ Options (in lexicographical order):)", printf(R"( --target-env= Set the target environment. Without this flag the target - enviroment defaults to spv1.3. must be one of + environment defaults to spv1.5. must be one of {%s})", target_env_list.c_str()); printf(R"( From 9e52bc0d0c1d6a54eedabdfc5f9850c8d2cba2f8 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 4 Feb 2020 16:21:10 +0000 Subject: [PATCH 15/88] utils/vscode: LSP - Support OpExtInst (#3140) This adds language server support for the `GLSL.std.450`, `OpenCL.std` and `OpenCL.DebugInfo.100` extension instructions. --- utils/vscode/spirv.json | 35 + utils/vscode/spirv.json.tmpl | 9 +- utils/vscode/src/parser/parser.go | 70 +- utils/vscode/src/schema/schema.go | 7541 +++++++++++++++++++----- utils/vscode/src/schema/schema.go.tmpl | 29 +- utils/vscode/src/tools/gen-grammar.go | 98 +- 6 files changed, 6381 insertions(+), 1401 deletions(-) diff --git a/utils/vscode/spirv.json b/utils/vscode/spirv.json index 30573d4550..7999b523ab 100644 --- a/utils/vscode/spirv.json +++ b/utils/vscode/spirv.json @@ -33,7 +33,14 @@ { "include": "#ValueEnum_GroupOperation" }, { "include": "#ValueEnum_KernelEnqueueFlags" }, { "include": "#ValueEnum_Capability" }, + { "include": "#BitEnum_DebugInfoFlags" }, + { "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" }, + { "include": "#ValueEnum_DebugCompositeType" }, + { "include": "#ValueEnum_DebugTypeQualifier" }, + { "include": "#ValueEnum_DebugOperation" }, + { "include": "#ValueEnum_DebugImportedEntity" }, { "include": "#opcode" }, + { "include": "#extopcode" }, { "include": "#identifier" }, { "include": "#number" }, { "include": "#string" }, @@ -161,10 +168,38 @@ "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b", "name": "keyword.spirv" }, + "BitEnum_DebugInfoFlags": { + "match": "\\b(FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugBaseTypeAttributeEncoding": { + "match": "\\b(Unspecified|Address|Boolean|Float|Signed|SignedChar|Unsigned|UnsignedChar)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugCompositeType": { + "match": "\\b(Class|Structure|Union)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugTypeQualifier": { + "match": "\\b(ConstType|VolatileType|RestrictType|AtomicType)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugOperation": { + "match": "\\b(Deref|Plus|Minus|PlusUconst|BitPiece|Swap|Xderef|StackValue|Constu|Fragment)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugImportedEntity": { + "match": "\\b(ImportedModule|ImportedDeclaration)\\b", + "name": "keyword.spirv" + }, "opcode": { "match": "(Op[a-zA-Z]+)", "name": "entity.name.function.spirv" }, + "extopcode": { + "match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource)", + "name": "entity.name.function.ext" + }, "identifier": { "match": "%[a-zA-Z0-9_]+", "name": "variable.spirv" diff --git a/utils/vscode/spirv.json.tmpl b/utils/vscode/spirv.json.tmpl index 8582d03fd4..f655e521d1 100644 --- a/utils/vscode/spirv.json.tmpl +++ b/utils/vscode/spirv.json.tmpl @@ -3,15 +3,16 @@ "name": "SPIR-V", "comment": "Generated by {{GenerateArguments}}. Do not modify this file directly.", "patterns": [ -{{range $o := .OperandKinds}}{{if len $o.Enumerants}} { "include": "#{{$o.Category}}_{{$o.Kind}}" }, +{{range $o := .All.OperandKinds}}{{if len $o.Enumerants}} { "include": "#{{$o.Category}}_{{$o.Kind}}" }, {{end}}{{end}} { "include": "#opcode" }, + { "include": "#extopcode" }, { "include": "#identifier" }, { "include": "#number" }, { "include": "#string" }, { "include": "#comment" }, { "include": "#operator" } ], - "repository": { {{range $o := .OperandKinds}}{{if len $o.Enumerants}} + "repository": { {{range $o := .All.OperandKinds}}{{if len $o.Enumerants}} "{{$o.Category}}_{{$o.Kind}}": { "match": "\\b({{OperandKindsMatch $o}})\\b", "name": "keyword.spirv" @@ -20,6 +21,10 @@ "match": "(Op[a-zA-Z]+)", "name": "entity.name.function.spirv" }, + "extopcode": { + "match": "({{AllExtOpcodes}})", + "name": "entity.name.function.ext" + }, "identifier": { "match": "%[a-zA-Z0-9_]+", "name": "variable.spirv" diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go index 64ba462d53..9f5691b2b7 100644 --- a/utils/vscode/src/parser/parser.go +++ b/utils/vscode/src/parser/parser.go @@ -396,12 +396,13 @@ func isAlpha(r rune) bool { return unicode.IsLetter(r) } func isAlphaNumeric(r rune) bool { return isAlpha(r) || isNumeric(r) } type parser struct { - lines []string // all source lines - toks []*Token // all tokens - diags []Diagnostic // parser emitted diagnostics - idents map[string]*Identifier // identifiers by name - mappings map[*Token]interface{} // tokens to semantic map - insts []*Instruction // all instructions + lines []string // all source lines + toks []*Token // all tokens + diags []Diagnostic // parser emitted diagnostics + idents map[string]*Identifier // identifiers by name + mappings map[*Token]interface{} // tokens to semantic map + extInstImports map[string]schema.OpcodeMap // extension imports by identifier + insts []*Instruction // all instructions } func (p *parser) parse() error { @@ -459,9 +460,9 @@ func (p *parser) instruction(i int) (n int) { p.addIdentDef(inst.Result.Text(p.lines), inst, p.tok(i)) } - for _, o := range operands { + processOperand := func(o schema.Operand) bool { if p.newline(i + n) { - break + return false } switch o.Quantifier { @@ -481,8 +482,35 @@ func (p *parser) instruction(i int) (n int) { inst.Tokens = append(inst.Tokens, op.Tokens...) n += c } else { - break + return false + } + } + } + return true + } + + for _, o := range operands { + if !processOperand(o) { + break + } + + if inst.Opcode == schema.OpExtInst && n == 4 { + extImportTok, extNameTok := p.tok(i+n), p.tok(i+n+1) + extImport := extImportTok.Text(p.lines) + if extOpcodes, ok := p.extInstImports[extImport]; ok { + extName := extNameTok.Text(p.lines) + if extOpcode, ok := extOpcodes[extName]; ok { + n += 2 // skip ext import, ext name + for _, o := range extOpcode.Operands { + if !processOperand(o) { + break + } + } + } else { + p.err(extNameTok, "Unknown extension opcode '%s'", extName) } + } else { + p.err(extImportTok, "Expected identifier to OpExtInstImport") } } } @@ -492,6 +520,19 @@ func (p *parser) instruction(i int) (n int) { } p.insts = append(p.insts, inst) + + if inst.Opcode == schema.OpExtInstImport && len(inst.Tokens) >= 4 { + // Instruction is a OpExtInstImport. Keep track of this. + extTok := inst.Tokens[3] + extName := strings.Trim(extTok.Text(p.lines), `"`) + extOpcodes, ok := schema.ExtOpcodes[extName] + if !ok { + p.err(extTok, "Unknown extension '%s'", extName) + } + extImport := inst.Result.Text(p.lines) + p.extInstImports[extImport] = extOpcodes + } + return } @@ -545,7 +586,7 @@ func (p *parser) operand(n string, k *schema.OperandKind, i int, optional bool) case schema.OperandCategoryLiteral: switch tok.Type { - case String, Integer, Float: + case String, Integer, Float, Ident: return op, 1 } if !optional { @@ -683,10 +724,11 @@ func Parse(source string) (Results, error) { } lines := strings.SplitAfter(source, "\n") p := parser{ - lines: lines, - toks: toks, - idents: map[string]*Identifier{}, - mappings: map[*Token]interface{}{}, + lines: lines, + toks: toks, + idents: map[string]*Identifier{}, + mappings: map[*Token]interface{}{}, + extInstImports: map[string]schema.OpcodeMap{}, } if err := p.parse(); err != nil { return Results{}, err diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go index c7e1ca4948..9f87f50c22 100755 --- a/utils/vscode/src/schema/schema.go +++ b/utils/vscode/src/schema/schema.go @@ -97,9 +97,11 @@ const ( OperandCategoryComposite = "Composite" ) +type OpcodeMap map[string]*Opcode + var ( // Opcodes is a map of opcode name to Opcode description. - Opcodes = map[string]*Opcode { + Opcodes = OpcodeMap { "OpNop": OpNop, "OpUndef": OpUndef, "OpSourceContinued": OpSourceContinued, @@ -627,6 +629,295 @@ var ( "OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL, } + // ExtOpcodes is a map of extension name to Opcode description list. + ExtOpcodes = map[string]OpcodeMap { + "GLSL.std.450": { + "Round": GLSLStd450_Round, + "RoundEven": GLSLStd450_RoundEven, + "Trunc": GLSLStd450_Trunc, + "FAbs": GLSLStd450_FAbs, + "SAbs": GLSLStd450_SAbs, + "FSign": GLSLStd450_FSign, + "SSign": GLSLStd450_SSign, + "Floor": GLSLStd450_Floor, + "Ceil": GLSLStd450_Ceil, + "Fract": GLSLStd450_Fract, + "Radians": GLSLStd450_Radians, + "Degrees": GLSLStd450_Degrees, + "Sin": GLSLStd450_Sin, + "Cos": GLSLStd450_Cos, + "Tan": GLSLStd450_Tan, + "Asin": GLSLStd450_Asin, + "Acos": GLSLStd450_Acos, + "Atan": GLSLStd450_Atan, + "Sinh": GLSLStd450_Sinh, + "Cosh": GLSLStd450_Cosh, + "Tanh": GLSLStd450_Tanh, + "Asinh": GLSLStd450_Asinh, + "Acosh": GLSLStd450_Acosh, + "Atanh": GLSLStd450_Atanh, + "Atan2": GLSLStd450_Atan2, + "Pow": GLSLStd450_Pow, + "Exp": GLSLStd450_Exp, + "Log": GLSLStd450_Log, + "Exp2": GLSLStd450_Exp2, + "Log2": GLSLStd450_Log2, + "Sqrt": GLSLStd450_Sqrt, + "InverseSqrt": GLSLStd450_InverseSqrt, + "Determinant": GLSLStd450_Determinant, + "MatrixInverse": GLSLStd450_MatrixInverse, + "Modf": GLSLStd450_Modf, + "ModfStruct": GLSLStd450_ModfStruct, + "FMin": GLSLStd450_FMin, + "UMin": GLSLStd450_UMin, + "SMin": GLSLStd450_SMin, + "FMax": GLSLStd450_FMax, + "UMax": GLSLStd450_UMax, + "SMax": GLSLStd450_SMax, + "FClamp": GLSLStd450_FClamp, + "UClamp": GLSLStd450_UClamp, + "SClamp": GLSLStd450_SClamp, + "FMix": GLSLStd450_FMix, + "IMix": GLSLStd450_IMix, + "Step": GLSLStd450_Step, + "SmoothStep": GLSLStd450_SmoothStep, + "Fma": GLSLStd450_Fma, + "Frexp": GLSLStd450_Frexp, + "FrexpStruct": GLSLStd450_FrexpStruct, + "Ldexp": GLSLStd450_Ldexp, + "PackSnorm4x8": GLSLStd450_PackSnorm4x8, + "PackUnorm4x8": GLSLStd450_PackUnorm4x8, + "PackSnorm2x16": GLSLStd450_PackSnorm2x16, + "PackUnorm2x16": GLSLStd450_PackUnorm2x16, + "PackHalf2x16": GLSLStd450_PackHalf2x16, + "PackDouble2x32": GLSLStd450_PackDouble2x32, + "UnpackSnorm2x16": GLSLStd450_UnpackSnorm2x16, + "UnpackUnorm2x16": GLSLStd450_UnpackUnorm2x16, + "UnpackHalf2x16": GLSLStd450_UnpackHalf2x16, + "UnpackSnorm4x8": GLSLStd450_UnpackSnorm4x8, + "UnpackUnorm4x8": GLSLStd450_UnpackUnorm4x8, + "UnpackDouble2x32": GLSLStd450_UnpackDouble2x32, + "Length": GLSLStd450_Length, + "Distance": GLSLStd450_Distance, + "Cross": GLSLStd450_Cross, + "Normalize": GLSLStd450_Normalize, + "FaceForward": GLSLStd450_FaceForward, + "Reflect": GLSLStd450_Reflect, + "Refract": GLSLStd450_Refract, + "FindILsb": GLSLStd450_FindILsb, + "FindSMsb": GLSLStd450_FindSMsb, + "FindUMsb": GLSLStd450_FindUMsb, + "InterpolateAtCentroid": GLSLStd450_InterpolateAtCentroid, + "InterpolateAtSample": GLSLStd450_InterpolateAtSample, + "InterpolateAtOffset": GLSLStd450_InterpolateAtOffset, + "NMin": GLSLStd450_NMin, + "NMax": GLSLStd450_NMax, + "NClamp": GLSLStd450_NClamp, + }, + "OpenCL.std": { + "acos": OpenCLStd_acos, + "acosh": OpenCLStd_acosh, + "acospi": OpenCLStd_acospi, + "asin": OpenCLStd_asin, + "asinh": OpenCLStd_asinh, + "asinpi": OpenCLStd_asinpi, + "atan": OpenCLStd_atan, + "atan2": OpenCLStd_atan2, + "atanh": OpenCLStd_atanh, + "atanpi": OpenCLStd_atanpi, + "atan2pi": OpenCLStd_atan2pi, + "cbrt": OpenCLStd_cbrt, + "ceil": OpenCLStd_ceil, + "copysign": OpenCLStd_copysign, + "cos": OpenCLStd_cos, + "cosh": OpenCLStd_cosh, + "cospi": OpenCLStd_cospi, + "erfc": OpenCLStd_erfc, + "erf": OpenCLStd_erf, + "exp": OpenCLStd_exp, + "exp2": OpenCLStd_exp2, + "exp10": OpenCLStd_exp10, + "expm1": OpenCLStd_expm1, + "fabs": OpenCLStd_fabs, + "fdim": OpenCLStd_fdim, + "floor": OpenCLStd_floor, + "fma": OpenCLStd_fma, + "fmax": OpenCLStd_fmax, + "fmin": OpenCLStd_fmin, + "fmod": OpenCLStd_fmod, + "fract": OpenCLStd_fract, + "frexp": OpenCLStd_frexp, + "hypot": OpenCLStd_hypot, + "ilogb": OpenCLStd_ilogb, + "ldexp": OpenCLStd_ldexp, + "lgamma": OpenCLStd_lgamma, + "lgamma_r": OpenCLStd_lgamma_r, + "log": OpenCLStd_log, + "log2": OpenCLStd_log2, + "log10": OpenCLStd_log10, + "log1p": OpenCLStd_log1p, + "logb": OpenCLStd_logb, + "mad": OpenCLStd_mad, + "maxmag": OpenCLStd_maxmag, + "minmag": OpenCLStd_minmag, + "modf": OpenCLStd_modf, + "nan": OpenCLStd_nan, + "nextafter": OpenCLStd_nextafter, + "pow": OpenCLStd_pow, + "pown": OpenCLStd_pown, + "powr": OpenCLStd_powr, + "remainder": OpenCLStd_remainder, + "remquo": OpenCLStd_remquo, + "rint": OpenCLStd_rint, + "rootn": OpenCLStd_rootn, + "round": OpenCLStd_round, + "rsqrt": OpenCLStd_rsqrt, + "sin": OpenCLStd_sin, + "sincos": OpenCLStd_sincos, + "sinh": OpenCLStd_sinh, + "sinpi": OpenCLStd_sinpi, + "sqrt": OpenCLStd_sqrt, + "tan": OpenCLStd_tan, + "tanh": OpenCLStd_tanh, + "tanpi": OpenCLStd_tanpi, + "tgamma": OpenCLStd_tgamma, + "trunc": OpenCLStd_trunc, + "half_cos": OpenCLStd_half_cos, + "half_divide": OpenCLStd_half_divide, + "half_exp": OpenCLStd_half_exp, + "half_exp2": OpenCLStd_half_exp2, + "half_exp10": OpenCLStd_half_exp10, + "half_log": OpenCLStd_half_log, + "half_log2": OpenCLStd_half_log2, + "half_log10": OpenCLStd_half_log10, + "half_powr": OpenCLStd_half_powr, + "half_recip": OpenCLStd_half_recip, + "half_rsqrt": OpenCLStd_half_rsqrt, + "half_sin": OpenCLStd_half_sin, + "half_sqrt": OpenCLStd_half_sqrt, + "half_tan": OpenCLStd_half_tan, + "native_cos": OpenCLStd_native_cos, + "native_divide": OpenCLStd_native_divide, + "native_exp": OpenCLStd_native_exp, + "native_exp2": OpenCLStd_native_exp2, + "native_exp10": OpenCLStd_native_exp10, + "native_log": OpenCLStd_native_log, + "native_log2": OpenCLStd_native_log2, + "native_log10": OpenCLStd_native_log10, + "native_powr": OpenCLStd_native_powr, + "native_recip": OpenCLStd_native_recip, + "native_rsqrt": OpenCLStd_native_rsqrt, + "native_sin": OpenCLStd_native_sin, + "native_sqrt": OpenCLStd_native_sqrt, + "native_tan": OpenCLStd_native_tan, + "s_abs": OpenCLStd_s_abs, + "s_abs_diff": OpenCLStd_s_abs_diff, + "s_add_sat": OpenCLStd_s_add_sat, + "u_add_sat": OpenCLStd_u_add_sat, + "s_hadd": OpenCLStd_s_hadd, + "u_hadd": OpenCLStd_u_hadd, + "s_rhadd": OpenCLStd_s_rhadd, + "u_rhadd": OpenCLStd_u_rhadd, + "s_clamp": OpenCLStd_s_clamp, + "u_clamp": OpenCLStd_u_clamp, + "clz": OpenCLStd_clz, + "ctz": OpenCLStd_ctz, + "s_mad_hi": OpenCLStd_s_mad_hi, + "u_mad_sat": OpenCLStd_u_mad_sat, + "s_mad_sat": OpenCLStd_s_mad_sat, + "s_max": OpenCLStd_s_max, + "u_max": OpenCLStd_u_max, + "s_min": OpenCLStd_s_min, + "u_min": OpenCLStd_u_min, + "s_mul_hi": OpenCLStd_s_mul_hi, + "rotate": OpenCLStd_rotate, + "s_sub_sat": OpenCLStd_s_sub_sat, + "u_sub_sat": OpenCLStd_u_sub_sat, + "u_upsample": OpenCLStd_u_upsample, + "s_upsample": OpenCLStd_s_upsample, + "popcount": OpenCLStd_popcount, + "s_mad24": OpenCLStd_s_mad24, + "u_mad24": OpenCLStd_u_mad24, + "s_mul24": OpenCLStd_s_mul24, + "u_mul24": OpenCLStd_u_mul24, + "u_abs": OpenCLStd_u_abs, + "u_abs_diff": OpenCLStd_u_abs_diff, + "u_mul_hi": OpenCLStd_u_mul_hi, + "u_mad_hi": OpenCLStd_u_mad_hi, + "fclamp": OpenCLStd_fclamp, + "degrees": OpenCLStd_degrees, + "fmax_common": OpenCLStd_fmax_common, + "fmin_common": OpenCLStd_fmin_common, + "mix": OpenCLStd_mix, + "radians": OpenCLStd_radians, + "step": OpenCLStd_step, + "smoothstep": OpenCLStd_smoothstep, + "sign": OpenCLStd_sign, + "cross": OpenCLStd_cross, + "distance": OpenCLStd_distance, + "length": OpenCLStd_length, + "normalize": OpenCLStd_normalize, + "fast_distance": OpenCLStd_fast_distance, + "fast_length": OpenCLStd_fast_length, + "fast_normalize": OpenCLStd_fast_normalize, + "bitselect": OpenCLStd_bitselect, + "select": OpenCLStd_select, + "vloadn": OpenCLStd_vloadn, + "vstoren": OpenCLStd_vstoren, + "vload_half": OpenCLStd_vload_half, + "vload_halfn": OpenCLStd_vload_halfn, + "vstore_half": OpenCLStd_vstore_half, + "vstore_half_r": OpenCLStd_vstore_half_r, + "vstore_halfn": OpenCLStd_vstore_halfn, + "vstore_halfn_r": OpenCLStd_vstore_halfn_r, + "vloada_halfn": OpenCLStd_vloada_halfn, + "vstorea_halfn": OpenCLStd_vstorea_halfn, + "vstorea_halfn_r": OpenCLStd_vstorea_halfn_r, + "shuffle": OpenCLStd_shuffle, + "shuffle2": OpenCLStd_shuffle2, + "printf": OpenCLStd_printf, + "prefetch": OpenCLStd_prefetch, + }, + "OpenCL.DebugInfo.100": { + "DebugInfoNone": OpenCLDebugInfo100_DebugInfoNone, + "DebugCompilationUnit": OpenCLDebugInfo100_DebugCompilationUnit, + "DebugTypeBasic": OpenCLDebugInfo100_DebugTypeBasic, + "DebugTypePointer": OpenCLDebugInfo100_DebugTypePointer, + "DebugTypeQualifier": OpenCLDebugInfo100_DebugTypeQualifier, + "DebugTypeArray": OpenCLDebugInfo100_DebugTypeArray, + "DebugTypeVector": OpenCLDebugInfo100_DebugTypeVector, + "DebugTypedef": OpenCLDebugInfo100_DebugTypedef, + "DebugTypeFunction": OpenCLDebugInfo100_DebugTypeFunction, + "DebugTypeEnum": OpenCLDebugInfo100_DebugTypeEnum, + "DebugTypeComposite": OpenCLDebugInfo100_DebugTypeComposite, + "DebugTypeMember": OpenCLDebugInfo100_DebugTypeMember, + "DebugTypeInheritance": OpenCLDebugInfo100_DebugTypeInheritance, + "DebugTypePtrToMember": OpenCLDebugInfo100_DebugTypePtrToMember, + "DebugTypeTemplate": OpenCLDebugInfo100_DebugTypeTemplate, + "DebugTypeTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateParameter, + "DebugTypeTemplateTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter, + "DebugTypeTemplateParameterPack": OpenCLDebugInfo100_DebugTypeTemplateParameterPack, + "DebugGlobalVariable": OpenCLDebugInfo100_DebugGlobalVariable, + "DebugFunctionDeclaration": OpenCLDebugInfo100_DebugFunctionDeclaration, + "DebugFunction": OpenCLDebugInfo100_DebugFunction, + "DebugLexicalBlock": OpenCLDebugInfo100_DebugLexicalBlock, + "DebugLexicalBlockDiscriminator": OpenCLDebugInfo100_DebugLexicalBlockDiscriminator, + "DebugScope": OpenCLDebugInfo100_DebugScope, + "DebugNoScope": OpenCLDebugInfo100_DebugNoScope, + "DebugInlinedAt": OpenCLDebugInfo100_DebugInlinedAt, + "DebugLocalVariable": OpenCLDebugInfo100_DebugLocalVariable, + "DebugInlinedVariable": OpenCLDebugInfo100_DebugInlinedVariable, + "DebugDeclare": OpenCLDebugInfo100_DebugDeclare, + "DebugValue": OpenCLDebugInfo100_DebugValue, + "DebugOperation": OpenCLDebugInfo100_DebugOperation, + "DebugExpression": OpenCLDebugInfo100_DebugExpression, + "DebugMacroDef": OpenCLDebugInfo100_DebugMacroDef, + "DebugMacroUndef": OpenCLDebugInfo100_DebugMacroUndef, + "DebugImportedEntity": OpenCLDebugInfo100_DebugImportedEntity, + "DebugSource": OpenCLDebugInfo100_DebugSource, + }, + } + OpNop = &Opcode { Opname: "OpNop", Operands: []Operand { @@ -13353,246 +13644,4407 @@ var ( }, } - - OperandKindImageOperands = &OperandKind { - Kind: "ImageOperands", - Category: "BitEnum", - Enumerants: []Enumerant { - Enumerant{ - Enumerant: "None", - Value: 0x0000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "Bias", - Value: 0x0001, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "Lod", - Value: 0x0002, - Capabilities: []string{}, - Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "Grad", - Value: 0x0004, - Capabilities: []string{}, - Parameters: []Parameter{{OperandKindIdRef, ""},{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "ConstOffset", - Value: 0x0008, - Capabilities: []string{}, - Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "Offset", - Value: 0x0010, - Capabilities: []string{"ImageGatherExtended",}, - Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "ConstOffsets", - Value: 0x0020, - Capabilities: []string{"ImageGatherExtended",}, - Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "Sample", - Value: 0x0040, - Capabilities: []string{}, - Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "MinLod", - Value: 0x0080, - Capabilities: []string{"MinLod",}, - Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", - }, - Enumerant{ - Enumerant: "MakeTexelAvailable", - Value: 0x0100, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "MakeTexelAvailableKHR", - Value: 0x0100, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "MakeTexelVisible", - Value: 0x0200, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "MakeTexelVisibleKHR", - Value: 0x0200, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "NonPrivateTexel", - Value: 0x0400, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "NonPrivateTexelKHR", - Value: 0x0400, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "VolatileTexel", - Value: 0x0800, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "VolatileTexelKHR", - Value: 0x0800, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "SignExtend", - Value: 0x1000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "1.4", - }, - Enumerant{ - Enumerant: "ZeroExtend", - Value: 0x2000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "1.4", - }, + GLSLStd450_Round = &Opcode { + Opname: "Round", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, }, - Bases: []*OperandKind {}, } - OperandKindFPFastMathMode = &OperandKind { - Kind: "FPFastMathMode", - Category: "BitEnum", - Enumerants: []Enumerant { - Enumerant{ - Enumerant: "None", - Value: 0x0000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "NotNaN", - Value: 0x0001, - Capabilities: []string{"Kernel",}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "NotInf", - Value: 0x0002, - Capabilities: []string{"Kernel",}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "NSZ", - Value: 0x0004, - Capabilities: []string{"Kernel",}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "AllowRecip", - Value: 0x0008, - Capabilities: []string{"Kernel",}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "Fast", - Value: 0x0010, - Capabilities: []string{"Kernel",}, - Parameters: []Parameter{}, - Version: "", - }, + GLSLStd450_RoundEven = &Opcode { + Opname: "RoundEven", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, }, - Bases: []*OperandKind {}, } - OperandKindSelectionControl = &OperandKind { - Kind: "SelectionControl", - Category: "BitEnum", - Enumerants: []Enumerant { - Enumerant{ - Enumerant: "None", - Value: 0x0000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "Flatten", - Value: 0x0001, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "DontFlatten", - Value: 0x0002, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, + GLSLStd450_Trunc = &Opcode { + Opname: "Trunc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, }, - Bases: []*OperandKind {}, } - OperandKindLoopControl = &OperandKind { - Kind: "LoopControl", - Category: "BitEnum", - Enumerants: []Enumerant { - Enumerant{ - Enumerant: "None", - Value: 0x0000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "Unroll", - Value: 0x0001, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "DontUnroll", - Value: 0x0002, - Capabilities: []string{}, + GLSLStd450_FAbs = &Opcode { + Opname: "FAbs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SAbs = &Opcode { + Opname: "SAbs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FSign = &Opcode { + Opname: "FSign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SSign = &Opcode { + Opname: "SSign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Floor = &Opcode { + Opname: "Floor", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Ceil = &Opcode { + Opname: "Ceil", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Fract = &Opcode { + Opname: "Fract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Radians = &Opcode { + Opname: "Radians", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'degrees'", + Quantifier: "", + }, + }, + } + GLSLStd450_Degrees = &Opcode { + Opname: "Degrees", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'radians'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sin = &Opcode { + Opname: "Sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cos = &Opcode { + Opname: "Cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Tan = &Opcode { + Opname: "Tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Asin = &Opcode { + Opname: "Asin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Acos = &Opcode { + Opname: "Acos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atan = &Opcode { + Opname: "Atan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y_over_x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sinh = &Opcode { + Opname: "Sinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cosh = &Opcode { + Opname: "Cosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Tanh = &Opcode { + Opname: "Tanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Asinh = &Opcode { + Opname: "Asinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Acosh = &Opcode { + Opname: "Acosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atanh = &Opcode { + Opname: "Atanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atan2 = &Opcode { + Opname: "Atan2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Pow = &Opcode { + Opname: "Pow", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_Exp = &Opcode { + Opname: "Exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Log = &Opcode { + Opname: "Log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Exp2 = &Opcode { + Opname: "Exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Log2 = &Opcode { + Opname: "Log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sqrt = &Opcode { + Opname: "Sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_InverseSqrt = &Opcode { + Opname: "InverseSqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Determinant = &Opcode { + Opname: "Determinant", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_MatrixInverse = &Opcode { + Opname: "MatrixInverse", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Modf = &Opcode { + Opname: "Modf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'i'", + Quantifier: "", + }, + }, + } + GLSLStd450_ModfStruct = &Opcode { + Opname: "ModfStruct", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMin = &Opcode { + Opname: "FMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_UMin = &Opcode { + Opname: "UMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_SMin = &Opcode { + Opname: "SMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMax = &Opcode { + Opname: "FMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_UMax = &Opcode { + Opname: "UMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_SMax = &Opcode { + Opname: "SMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_FClamp = &Opcode { + Opname: "FClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_UClamp = &Opcode { + Opname: "UClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_SClamp = &Opcode { + Opname: "SClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMix = &Opcode { + Opname: "FMix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + GLSLStd450_IMix = &Opcode { + Opname: "IMix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + GLSLStd450_Step = &Opcode { + Opname: "Step", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SmoothStep = &Opcode { + Opname: "SmoothStep", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'edge1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Fma = &Opcode { + Opname: "Fma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + GLSLStd450_Frexp = &Opcode { + Opname: "Frexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + GLSLStd450_FrexpStruct = &Opcode { + Opname: "FrexpStruct", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Ldexp = &Opcode { + Opname: "Ldexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackSnorm4x8 = &Opcode { + Opname: "PackSnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackUnorm4x8 = &Opcode { + Opname: "PackUnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackSnorm2x16 = &Opcode { + Opname: "PackSnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackUnorm2x16 = &Opcode { + Opname: "PackUnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackHalf2x16 = &Opcode { + Opname: "PackHalf2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackDouble2x32 = &Opcode { + Opname: "PackDouble2x32", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackSnorm2x16 = &Opcode { + Opname: "UnpackSnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackUnorm2x16 = &Opcode { + Opname: "UnpackUnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackHalf2x16 = &Opcode { + Opname: "UnpackHalf2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackSnorm4x8 = &Opcode { + Opname: "UnpackSnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackUnorm4x8 = &Opcode { + Opname: "UnpackUnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackDouble2x32 = &Opcode { + Opname: "UnpackDouble2x32", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_Length = &Opcode { + Opname: "Length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Distance = &Opcode { + Opname: "Distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cross = &Opcode { + Opname: "Cross", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_Normalize = &Opcode { + Opname: "Normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FaceForward = &Opcode { + Opname: "FaceForward", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Nref'", + Quantifier: "", + }, + }, + } + GLSLStd450_Reflect = &Opcode { + Opname: "Reflect", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + }, + } + GLSLStd450_Refract = &Opcode { + Opname: "Refract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'eta'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindILsb = &Opcode { + Opname: "FindILsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindSMsb = &Opcode { + Opname: "FindSMsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindUMsb = &Opcode { + Opname: "FindUMsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtCentroid = &Opcode { + Opname: "InterpolateAtCentroid", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtSample = &Opcode { + Opname: "InterpolateAtSample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'sample'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtOffset = &Opcode { + Opname: "InterpolateAtOffset", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + }, + } + GLSLStd450_NMin = &Opcode { + Opname: "NMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_NMax = &Opcode { + Opname: "NMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_NClamp = &Opcode { + Opname: "NClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + OpenCLStd_acos = &Opcode { + Opname: "acos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_acosh = &Opcode { + Opname: "acosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_acospi = &Opcode { + Opname: "acospi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asin = &Opcode { + Opname: "asin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asinh = &Opcode { + Opname: "asinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asinpi = &Opcode { + Opname: "asinpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan = &Opcode { + Opname: "atan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan2 = &Opcode { + Opname: "atan2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atanh = &Opcode { + Opname: "atanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atanpi = &Opcode { + Opname: "atanpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan2pi = &Opcode { + Opname: "atan2pi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cbrt = &Opcode { + Opname: "cbrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ceil = &Opcode { + Opname: "ceil", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_copysign = &Opcode { + Opname: "copysign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_cos = &Opcode { + Opname: "cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cosh = &Opcode { + Opname: "cosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cospi = &Opcode { + Opname: "cospi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_erfc = &Opcode { + Opname: "erfc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_erf = &Opcode { + Opname: "erf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp = &Opcode { + Opname: "exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp2 = &Opcode { + Opname: "exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp10 = &Opcode { + Opname: "exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_expm1 = &Opcode { + Opname: "expm1", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fabs = &Opcode { + Opname: "fabs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fdim = &Opcode { + Opname: "fdim", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_floor = &Opcode { + Opname: "floor", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fma = &Opcode { + Opname: "fma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmax = &Opcode { + Opname: "fmax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmin = &Opcode { + Opname: "fmin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmod = &Opcode { + Opname: "fmod", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fract = &Opcode { + Opname: "fract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ptr'", + Quantifier: "", + }, + }, + } + OpenCLStd_frexp = &Opcode { + Opname: "frexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + OpenCLStd_hypot = &Opcode { + Opname: "hypot", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_ilogb = &Opcode { + Opname: "ilogb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ldexp = &Opcode { + Opname: "ldexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'k'", + Quantifier: "", + }, + }, + } + OpenCLStd_lgamma = &Opcode { + Opname: "lgamma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_lgamma_r = &Opcode { + Opname: "lgamma_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'signp'", + Quantifier: "", + }, + }, + } + OpenCLStd_log = &Opcode { + Opname: "log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log2 = &Opcode { + Opname: "log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log10 = &Opcode { + Opname: "log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log1p = &Opcode { + Opname: "log1p", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_logb = &Opcode { + Opname: "logb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_mad = &Opcode { + Opname: "mad", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_maxmag = &Opcode { + Opname: "maxmag", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_minmag = &Opcode { + Opname: "minmag", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_modf = &Opcode { + Opname: "modf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'iptr'", + Quantifier: "", + }, + }, + } + OpenCLStd_nan = &Opcode { + Opname: "nan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'nancode'", + Quantifier: "", + }, + }, + } + OpenCLStd_nextafter = &Opcode { + Opname: "nextafter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_pow = &Opcode { + Opname: "pow", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y", + Quantifier: "", + }, + }, + } + OpenCLStd_pown = &Opcode { + Opname: "pown", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_powr = &Opcode { + Opname: "powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_remainder = &Opcode { + Opname: "remainder", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_remquo = &Opcode { + Opname: "remquo", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'quo'", + Quantifier: "", + }, + }, + } + OpenCLStd_rint = &Opcode { + Opname: "rint", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_rootn = &Opcode { + Opname: "rootn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_round = &Opcode { + Opname: "round", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_rsqrt = &Opcode { + Opname: "rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sin = &Opcode { + Opname: "sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sincos = &Opcode { + Opname: "sincos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'cosval'", + Quantifier: "", + }, + }, + } + OpenCLStd_sinh = &Opcode { + Opname: "sinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sinpi = &Opcode { + Opname: "sinpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sqrt = &Opcode { + Opname: "sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tan = &Opcode { + Opname: "tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tanh = &Opcode { + Opname: "tanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tanpi = &Opcode { + Opname: "tanpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tgamma = &Opcode { + Opname: "tgamma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_trunc = &Opcode { + Opname: "trunc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_cos = &Opcode { + Opname: "half_cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_divide = &Opcode { + Opname: "half_divide", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp = &Opcode { + Opname: "half_exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp2 = &Opcode { + Opname: "half_exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp10 = &Opcode { + Opname: "half_exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log = &Opcode { + Opname: "half_log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log2 = &Opcode { + Opname: "half_log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log10 = &Opcode { + Opname: "half_log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_powr = &Opcode { + Opname: "half_powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_recip = &Opcode { + Opname: "half_recip", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_rsqrt = &Opcode { + Opname: "half_rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_sin = &Opcode { + Opname: "half_sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_sqrt = &Opcode { + Opname: "half_sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_tan = &Opcode { + Opname: "half_tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_cos = &Opcode { + Opname: "native_cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_divide = &Opcode { + Opname: "native_divide", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp = &Opcode { + Opname: "native_exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp2 = &Opcode { + Opname: "native_exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp10 = &Opcode { + Opname: "native_exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log = &Opcode { + Opname: "native_log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log2 = &Opcode { + Opname: "native_log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log10 = &Opcode { + Opname: "native_log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_powr = &Opcode { + Opname: "native_powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_recip = &Opcode { + Opname: "native_recip", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_rsqrt = &Opcode { + Opname: "native_rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_sin = &Opcode { + Opname: "native_sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_sqrt = &Opcode { + Opname: "native_sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_tan = &Opcode { + Opname: "native_tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_abs = &Opcode { + Opname: "s_abs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_abs_diff = &Opcode { + Opname: "s_abs_diff", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_add_sat = &Opcode { + Opname: "s_add_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_add_sat = &Opcode { + Opname: "u_add_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_hadd = &Opcode { + Opname: "s_hadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_hadd = &Opcode { + Opname: "u_hadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_rhadd = &Opcode { + Opname: "s_rhadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_rhadd = &Opcode { + Opname: "u_rhadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_clamp = &Opcode { + Opname: "s_clamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_clamp = &Opcode { + Opname: "u_clamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_clz = &Opcode { + Opname: "clz", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ctz = &Opcode { + Opname: "ctz", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad_hi = &Opcode { + Opname: "s_mad_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad_sat = &Opcode { + Opname: "u_mad_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad_sat = &Opcode { + Opname: "s_mad_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_max = &Opcode { + Opname: "s_max", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_max = &Opcode { + Opname: "u_max", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_min = &Opcode { + Opname: "s_min", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_min = &Opcode { + Opname: "u_min", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mul_hi = &Opcode { + Opname: "s_mul_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_rotate = &Opcode { + Opname: "rotate", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'i'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_sub_sat = &Opcode { + Opname: "s_sub_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_sub_sat = &Opcode { + Opname: "u_sub_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_upsample = &Opcode { + Opname: "u_upsample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'hi'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'lo'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_upsample = &Opcode { + Opname: "s_upsample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'hi'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'lo'", + Quantifier: "", + }, + }, + } + OpenCLStd_popcount = &Opcode { + Opname: "popcount", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad24 = &Opcode { + Opname: "s_mad24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad24 = &Opcode { + Opname: "u_mad24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mul24 = &Opcode { + Opname: "s_mul24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mul24 = &Opcode { + Opname: "u_mul24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_abs = &Opcode { + Opname: "u_abs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_abs_diff = &Opcode { + Opname: "u_abs_diff", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mul_hi = &Opcode { + Opname: "u_mul_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad_hi = &Opcode { + Opname: "u_mad_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_fclamp = &Opcode { + Opname: "fclamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_degrees = &Opcode { + Opname: "degrees", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'radians'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmax_common = &Opcode { + Opname: "fmax_common", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmin_common = &Opcode { + Opname: "fmin_common", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_mix = &Opcode { + Opname: "mix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + OpenCLStd_radians = &Opcode { + Opname: "radians", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'degrees'", + Quantifier: "", + }, + }, + } + OpenCLStd_step = &Opcode { + Opname: "step", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_smoothstep = &Opcode { + Opname: "smoothstep", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'edge1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sign = &Opcode { + Opname: "sign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cross = &Opcode { + Opname: "cross", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_distance = &Opcode { + Opname: "distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_length = &Opcode { + Opname: "length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_normalize = &Opcode { + Opname: "normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_distance = &Opcode { + Opname: "fast_distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_length = &Opcode { + Opname: "fast_length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_normalize = &Opcode { + Opname: "fast_normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_bitselect = &Opcode { + Opname: "bitselect", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_select = &Opcode { + Opname: "select", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_vloadn = &Opcode { + Opname: "vloadn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstoren = &Opcode { + Opname: "vstoren", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vload_half = &Opcode { + Opname: "vload_half", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vload_halfn = &Opcode { + Opname: "vload_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_half = &Opcode { + Opname: "vstore_half", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_half_r = &Opcode { + Opname: "vstore_half_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_halfn = &Opcode { + Opname: "vstore_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_halfn_r = &Opcode { + Opname: "vstore_halfn_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_vloada_halfn = &Opcode { + Opname: "vloada_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstorea_halfn = &Opcode { + Opname: "vstorea_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstorea_halfn_r = &Opcode { + Opname: "vstorea_halfn_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_shuffle = &Opcode { + Opname: "shuffle", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'shuffle mask'", + Quantifier: "", + }, + }, + } + OpenCLStd_shuffle2 = &Opcode { + Opname: "shuffle2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'shuffle mask'", + Quantifier: "", + }, + }, + } + OpenCLStd_printf = &Opcode { + Opname: "printf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'format'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'additional arguments'", + Quantifier: "*", + }, + }, + } + OpenCLStd_prefetch = &Opcode { + Opname: "prefetch", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'ptr'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'num elements'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugInfoNone = &Opcode { + Opname: "DebugInfoNone", + Operands: []Operand { + }, + } + OpenCLDebugInfo100_DebugCompilationUnit = &Opcode { + Opname: "DebugCompilationUnit", + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'DWARF Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindSourceLanguage, + Name: "'Language'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeBasic = &Opcode { + Opname: "DebugTypeBasic", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugBaseTypeAttributeEncoding, + Name: "'Encoding'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypePointer = &Opcode { + Opname: "DebugTypePointer", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "'Storage Class'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeQualifier = &Opcode { + Opname: "DebugTypeQualifier", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugTypeQualifier, + Name: "'Type Qualifier'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeArray = &Opcode { + Opname: "DebugTypeArray", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Counts'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeVector = &Opcode { + Opname: "DebugTypeVector", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Component Count'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypedef = &Opcode { + Opname: "DebugTypedef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeFunction = &Opcode { + Opname: "DebugTypeFunction", + Operands: []Operand { + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Return Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameter Types'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeEnum = &Opcode { + Opname: "DebugTypeEnum", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Underlying Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairIdRefIdRef, + Name: "'Value, Name, Value, Name, ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeComposite = &Opcode { + Opname: "DebugTypeComposite", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugCompositeType, + Name: "'Tag'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Members'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeMember = &Opcode { + Opname: "DebugTypeMember", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugTypeInheritance = &Opcode { + Opname: "DebugTypeInheritance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Child'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypePtrToMember = &Opcode { + Opname: "DebugTypePtrToMember", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Member Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplate = &Opcode { + Opname: "DebugTypeTemplate", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameters'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateParameter = &Opcode { + Opname: "DebugTypeTemplateParameter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Actual Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter = &Opcode { + Opname: "DebugTypeTemplateTemplateParameter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Template Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateParameterPack = &Opcode { + Opname: "DebugTypeTemplateParameterPack", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Template Parameters'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugGlobalVariable = &Opcode { + Opname: "DebugGlobalVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Static Member Declaration'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugFunctionDeclaration = &Opcode { + Opname: "DebugFunctionDeclaration", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugFunction = &Opcode { + Opname: "DebugFunction", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Scope Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Declaration'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLexicalBlock = &Opcode { + Opname: "DebugLexicalBlock", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLexicalBlockDiscriminator = &Opcode { + Opname: "DebugLexicalBlockDiscriminator", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Discriminator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugScope = &Opcode { + Opname: "DebugScope", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined At'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugNoScope = &Opcode { + Opname: "DebugNoScope", + Operands: []Operand { + }, + } + OpenCLDebugInfo100_DebugInlinedAt = &Opcode { + Opname: "DebugInlinedAt", + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLocalVariable = &Opcode { + Opname: "DebugLocalVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Arg Number'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugInlinedVariable = &Opcode { + Opname: "DebugInlinedVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugDeclare = &Opcode { + Opname: "DebugDeclare", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Local Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Expression'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugValue = &Opcode { + Opname: "DebugValue", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Local Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Expression'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugOperation = &Opcode { + Opname: "DebugOperation", + Operands: []Operand { + Operand { + Kind: OperandKindDebugOperation, + Name: "'OpCode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Operands ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugExpression = &Opcode { + Opname: "DebugExpression", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Operands ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugMacroDef = &Opcode { + Opname: "DebugMacroDef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugMacroUndef = &Opcode { + Opname: "DebugMacroUndef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Macro'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugImportedEntity = &Opcode { + Opname: "DebugImportedEntity", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugImportedEntity, + Name: "'Tag'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Entity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugSource = &Opcode { + Opname: "DebugSource", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'File'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Text'", + Quantifier: "?", + }, + }, + } + + + OperandKindImageOperands = &OperandKind { + Kind: "ImageOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Bias", + Value: 0x0001, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Lod", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Grad", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "ConstOffset", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Offset", + Value: 0x0010, + Capabilities: []string{"ImageGatherExtended",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "ConstOffsets", + Value: 0x0020, + Capabilities: []string{"ImageGatherExtended",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Sample", + Value: 0x0040, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "MinLod", + Value: 0x0080, + Capabilities: []string{"MinLod",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "MakeTexelAvailable", + Value: 0x0100, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeTexelAvailableKHR", + Value: 0x0100, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeTexelVisible", + Value: 0x0200, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeTexelVisibleKHR", + Value: 0x0200, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivateTexel", + Value: 0x0400, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivateTexelKHR", + Value: 0x0400, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VolatileTexel", + Value: 0x0800, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VolatileTexelKHR", + Value: 0x0800, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "SignExtend", + Value: 0x1000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "ZeroExtend", + Value: 0x2000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPFastMathMode = &OperandKind { + Kind: "FPFastMathMode", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NotNaN", + Value: 0x0001, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NotInf", + Value: 0x0002, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NSZ", + Value: 0x0004, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AllowRecip", + Value: 0x0008, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Fast", + Value: 0x0010, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSelectionControl = &OperandKind { + Kind: "SelectionControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Flatten", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DontFlatten", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindLoopControl = &OperandKind { + Kind: "LoopControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Unroll", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DontUnroll", + Value: 0x0002, + Capabilities: []string{}, Parameters: []Parameter{}, Version: "", }, @@ -16047,2072 +20499,2429 @@ var ( Category: "ValueEnum", Enumerants: []Enumerant { Enumerant{ - Enumerant: "Position", - Value: 0, - Capabilities: []string{"Shader",}, + Enumerant: "Position", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PointSize", + Value: 1, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ClipDistance", + Value: 3, + Capabilities: []string{"ClipDistance",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CullDistance", + Value: 4, + Capabilities: []string{"CullDistance",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VertexId", + Value: 5, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InstanceId", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PrimitiveId", + Value: 7, + Capabilities: []string{"Geometry","Tessellation","RayTracingNV",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InvocationId", + Value: 8, + Capabilities: []string{"Geometry","Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Layer", + Value: 9, + Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ViewportIndex", + Value: 10, + Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessLevelOuter", + Value: 11, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessLevelInner", + Value: 12, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessCoord", + Value: 13, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PatchVertices", + Value: 14, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FragCoord", + Value: 15, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PointCoord", + Value: 16, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FrontFacing", + Value: 17, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampleId", + Value: 18, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SamplePosition", + Value: 19, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampleMask", + Value: 20, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FragDepth", + Value: 22, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "HelperInvocation", + Value: 23, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NumWorkgroups", + Value: 24, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WorkgroupSize", + Value: 25, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WorkgroupId", + Value: 26, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "LocalInvocationId", + Value: 27, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalInvocationId", + Value: 28, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "LocalInvocationIndex", + Value: 29, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WorkDim", + Value: 30, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalSize", + Value: 31, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "EnqueuedWorkgroupSize", + Value: 32, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalOffset", + Value: 33, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalLinearId", + Value: 34, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupSize", + Value: 36, + Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupMaxSize", + Value: 37, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NumSubgroups", + Value: 38, + Capabilities: []string{"Kernel","GroupNonUniform",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "PointSize", - Value: 1, - Capabilities: []string{"Shader",}, + Enumerant: "NumEnqueuedSubgroups", + Value: 39, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "ClipDistance", - Value: 3, - Capabilities: []string{"ClipDistance",}, + Enumerant: "SubgroupId", + Value: 40, + Capabilities: []string{"Kernel","GroupNonUniform",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "CullDistance", - Value: 4, - Capabilities: []string{"CullDistance",}, + Enumerant: "SubgroupLocalInvocationId", + Value: 41, + Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "VertexId", - Value: 5, + Enumerant: "VertexIndex", + Value: 42, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "InstanceId", - Value: 6, + Enumerant: "InstanceIndex", + Value: 43, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "PrimitiveId", - Value: 7, - Capabilities: []string{"Geometry","Tessellation","RayTracingNV",}, + Enumerant: "SubgroupEqMask", + Value: 4416, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "InvocationId", - Value: 8, - Capabilities: []string{"Geometry","Tessellation",}, + Enumerant: "SubgroupGeMask", + Value: 4417, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "Layer", - Value: 9, - Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT",}, + Enumerant: "SubgroupGtMask", + Value: 4418, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "ViewportIndex", - Value: 10, - Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT",}, + Enumerant: "SubgroupLeMask", + Value: 4419, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "TessLevelOuter", - Value: 11, - Capabilities: []string{"Tessellation",}, + Enumerant: "SubgroupLtMask", + Value: 4420, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "TessLevelInner", - Value: 12, - Capabilities: []string{"Tessellation",}, + Enumerant: "SubgroupEqMaskKHR", + Value: 4416, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "TessCoord", - Value: 13, - Capabilities: []string{"Tessellation",}, + Enumerant: "SubgroupGeMaskKHR", + Value: 4417, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "PatchVertices", - Value: 14, - Capabilities: []string{"Tessellation",}, + Enumerant: "SubgroupGtMaskKHR", + Value: 4418, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "FragCoord", - Value: 15, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupLeMaskKHR", + Value: 4419, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "PointCoord", - Value: 16, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupLtMaskKHR", + Value: 4420, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "FrontFacing", - Value: 17, - Capabilities: []string{"Shader",}, + Enumerant: "BaseVertex", + Value: 4424, + Capabilities: []string{"DrawParameters",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "SampleId", - Value: 18, - Capabilities: []string{"SampleRateShading",}, + Enumerant: "BaseInstance", + Value: 4425, + Capabilities: []string{"DrawParameters",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "SamplePosition", - Value: 19, - Capabilities: []string{"SampleRateShading",}, + Enumerant: "DrawIndex", + Value: 4426, + Capabilities: []string{"DrawParameters","MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "SampleMask", - Value: 20, - Capabilities: []string{"Shader",}, + Enumerant: "DeviceIndex", + Value: 4438, + Capabilities: []string{"DeviceGroup",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "FragDepth", - Value: 22, - Capabilities: []string{"Shader",}, + Enumerant: "ViewIndex", + Value: 4440, + Capabilities: []string{"MultiView",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "HelperInvocation", - Value: 23, - Capabilities: []string{"Shader",}, + Enumerant: "BaryCoordNoPerspAMD", + Value: 4992, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "NumWorkgroups", - Value: 24, + Enumerant: "BaryCoordNoPerspCentroidAMD", + Value: 4993, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "WorkgroupSize", - Value: 25, + Enumerant: "BaryCoordNoPerspSampleAMD", + Value: 4994, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "WorkgroupId", - Value: 26, + Enumerant: "BaryCoordSmoothAMD", + Value: 4995, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "LocalInvocationId", - Value: 27, + Enumerant: "BaryCoordSmoothCentroidAMD", + Value: 4996, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "GlobalInvocationId", - Value: 28, + Enumerant: "BaryCoordSmoothSampleAMD", + Value: 4997, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "LocalInvocationIndex", - Value: 29, + Enumerant: "BaryCoordPullModelAMD", + Value: 4998, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "WorkDim", - Value: 30, - Capabilities: []string{"Kernel",}, + Enumerant: "FragStencilRefEXT", + Value: 5014, + Capabilities: []string{"StencilExportEXT",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "GlobalSize", - Value: 31, - Capabilities: []string{"Kernel",}, + Enumerant: "ViewportMaskNV", + Value: 5253, + Capabilities: []string{"ShaderViewportMaskNV","MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "EnqueuedWorkgroupSize", - Value: 32, - Capabilities: []string{"Kernel",}, + Enumerant: "SecondaryPositionNV", + Value: 5257, + Capabilities: []string{"ShaderStereoViewNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", + }, + Enumerant{ + Enumerant: "SecondaryViewportMaskNV", + Value: 5258, + Capabilities: []string{"ShaderStereoViewNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PositionPerViewNV", + Value: 5261, + Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ViewportMaskPerViewNV", + Value: 5262, + Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FullyCoveredEXT", + Value: 5264, + Capabilities: []string{"FragmentFullyCoveredEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TaskCountNV", + Value: 5274, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", }, Enumerant{ - Enumerant: "GlobalOffset", - Value: 33, - Capabilities: []string{"Kernel",}, + Enumerant: "PrimitiveCountNV", + Value: 5275, + Capabilities: []string{"MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "GlobalLinearId", - Value: 34, - Capabilities: []string{"Kernel",}, + Enumerant: "PrimitiveIndicesNV", + Value: 5276, + Capabilities: []string{"MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupSize", - Value: 36, - Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, + Enumerant: "ClipDistancePerViewNV", + Value: 5277, + Capabilities: []string{"MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupMaxSize", - Value: 37, - Capabilities: []string{"Kernel",}, + Enumerant: "CullDistancePerViewNV", + Value: 5278, + Capabilities: []string{"MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "NumSubgroups", - Value: 38, - Capabilities: []string{"Kernel","GroupNonUniform",}, + Enumerant: "LayerPerViewNV", + Value: 5279, + Capabilities: []string{"MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "NumEnqueuedSubgroups", - Value: 39, - Capabilities: []string{"Kernel",}, + Enumerant: "MeshViewCountNV", + Value: 5280, + Capabilities: []string{"MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupId", - Value: 40, - Capabilities: []string{"Kernel","GroupNonUniform",}, + Enumerant: "MeshViewIndicesNV", + Value: 5281, + Capabilities: []string{"MeshShadingNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupLocalInvocationId", - Value: 41, - Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, + Enumerant: "BaryCoordNV", + Value: 5286, + Capabilities: []string{"FragmentBarycentricNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "VertexIndex", - Value: 42, - Capabilities: []string{"Shader",}, + Enumerant: "BaryCoordNoPerspNV", + Value: 5287, + Capabilities: []string{"FragmentBarycentricNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "InstanceIndex", - Value: 43, - Capabilities: []string{"Shader",}, + Enumerant: "FragSizeEXT", + Value: 5292, + Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupEqMask", - Value: 4416, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "FragmentSizeNV", + Value: 5292, + Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupGeMask", - Value: 4417, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "FragInvocationCountEXT", + Value: 5293, + Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupGtMask", - Value: 4418, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "InvocationsPerPixelNV", + Value: 5293, + Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupLeMask", - Value: 4419, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "LaunchIdNV", + Value: 5319, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupLtMask", - Value: 4420, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "LaunchSizeNV", + Value: 5320, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupEqMaskKHR", - Value: 4416, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "WorldRayOriginNV", + Value: 5321, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupGeMaskKHR", - Value: 4417, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "WorldRayDirectionNV", + Value: 5322, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupGtMaskKHR", - Value: 4418, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "ObjectRayOriginNV", + Value: 5323, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupLeMaskKHR", - Value: 4419, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "ObjectRayDirectionNV", + Value: 5324, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupLtMaskKHR", - Value: 4420, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Enumerant: "RayTminNV", + Value: 5325, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "BaseVertex", - Value: 4424, - Capabilities: []string{"DrawParameters",}, + Enumerant: "RayTmaxNV", + Value: 5326, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "BaseInstance", - Value: 4425, - Capabilities: []string{"DrawParameters",}, + Enumerant: "InstanceCustomIndexNV", + Value: 5327, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "DrawIndex", - Value: 4426, - Capabilities: []string{"DrawParameters","MeshShadingNV",}, + Enumerant: "ObjectToWorldNV", + Value: 5330, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "DeviceIndex", - Value: 4438, - Capabilities: []string{"DeviceGroup",}, + Enumerant: "WorldToObjectNV", + Value: 5331, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "ViewIndex", - Value: 4440, - Capabilities: []string{"MultiView",}, + Enumerant: "HitTNV", + Value: 5332, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "BaryCoordNoPerspAMD", - Value: 4992, - Capabilities: []string{}, + Enumerant: "HitKindNV", + Value: 5333, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "BaryCoordNoPerspCentroidAMD", - Value: 4993, - Capabilities: []string{}, + Enumerant: "IncomingRayFlagsNV", + Value: 5351, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "BaryCoordNoPerspSampleAMD", - Value: 4994, - Capabilities: []string{}, + Enumerant: "WarpsPerSMNV", + Value: 5374, + Capabilities: []string{"ShaderSMBuiltinsNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "BaryCoordSmoothAMD", - Value: 4995, - Capabilities: []string{}, + Enumerant: "SMCountNV", + Value: 5375, + Capabilities: []string{"ShaderSMBuiltinsNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "BaryCoordSmoothCentroidAMD", - Value: 4996, - Capabilities: []string{}, + Enumerant: "WarpIDNV", + Value: 5376, + Capabilities: []string{"ShaderSMBuiltinsNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "BaryCoordSmoothSampleAMD", - Value: 4997, - Capabilities: []string{}, + Enumerant: "SMIDNV", + Value: 5377, + Capabilities: []string{"ShaderSMBuiltinsNV",}, Parameters: []Parameter{}, Version: "None", }, + }, + Bases: []*OperandKind {}, + } + OperandKindScope = &OperandKind { + Kind: "Scope", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "BaryCoordPullModelAMD", - Value: 4998, + Enumerant: "CrossDevice", + Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "FragStencilRefEXT", - Value: 5014, - Capabilities: []string{"StencilExportEXT",}, + Enumerant: "Device", + Value: 1, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "ViewportMaskNV", - Value: 5253, - Capabilities: []string{"ShaderViewportMaskNV","MeshShadingNV",}, + Enumerant: "Workgroup", + Value: 2, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SecondaryPositionNV", - Value: 5257, - Capabilities: []string{"ShaderStereoViewNV",}, + Enumerant: "Subgroup", + Value: 3, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SecondaryViewportMaskNV", - Value: 5258, - Capabilities: []string{"ShaderStereoViewNV",}, + Enumerant: "Invocation", + Value: 4, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "PositionPerViewNV", - Value: 5261, - Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Enumerant: "QueueFamily", + Value: 5, + Capabilities: []string{"VulkanMemoryModel",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "ViewportMaskPerViewNV", - Value: 5262, - Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Enumerant: "QueueFamilyKHR", + Value: 5, + Capabilities: []string{"VulkanMemoryModel",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, + }, + Bases: []*OperandKind {}, + } + OperandKindGroupOperation = &OperandKind { + Kind: "GroupOperation", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "FullyCoveredEXT", - Value: 5264, - Capabilities: []string{"FragmentFullyCoveredEXT",}, + Enumerant: "Reduce", + Value: 0, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "TaskCountNV", - Value: 5274, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "InclusiveScan", + Value: 1, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "PrimitiveCountNV", - Value: 5275, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "ExclusiveScan", + Value: 2, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "PrimitiveIndicesNV", - Value: 5276, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "ClusteredReduce", + Value: 3, + Capabilities: []string{"GroupNonUniformClustered",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.3", }, Enumerant{ - Enumerant: "ClipDistancePerViewNV", - Value: 5277, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "PartitionedReduceNV", + Value: 6, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "CullDistancePerViewNV", - Value: 5278, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "PartitionedInclusiveScanNV", + Value: 7, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "LayerPerViewNV", - Value: 5279, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "PartitionedExclusiveScanNV", + Value: 8, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, Parameters: []Parameter{}, Version: "None", }, + }, + Bases: []*OperandKind {}, + } + OperandKindKernelEnqueueFlags = &OperandKind { + Kind: "KernelEnqueueFlags", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "MeshViewCountNV", - Value: 5280, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "NoWait", + Value: 0, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "MeshViewIndicesNV", - Value: 5281, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "WaitKernel", + Value: 1, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "BaryCoordNV", - Value: 5286, - Capabilities: []string{"FragmentBarycentricNV",}, + Enumerant: "WaitWorkGroup", + Value: 2, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, + }, + Bases: []*OperandKind {}, + } + OperandKindCapability = &OperandKind { + Kind: "Capability", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "BaryCoordNoPerspNV", - Value: 5287, - Capabilities: []string{"FragmentBarycentricNV",}, + Enumerant: "Matrix", + Value: 0, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "FragSizeEXT", - Value: 5292, - Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, + Enumerant: "Shader", + Value: 1, + Capabilities: []string{"Matrix",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "FragmentSizeNV", - Value: 5292, - Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, + Enumerant: "Geometry", + Value: 2, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "FragInvocationCountEXT", - Value: 5293, - Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, + Enumerant: "Tessellation", + Value: 3, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "InvocationsPerPixelNV", - Value: 5293, - Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, + Enumerant: "Addresses", + Value: 4, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "LaunchIdNV", - Value: 5319, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Linkage", + Value: 5, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "LaunchSizeNV", - Value: 5320, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Kernel", + Value: 6, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "WorldRayOriginNV", - Value: 5321, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Vector16", + Value: 7, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "WorldRayDirectionNV", - Value: 5322, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Float16Buffer", + Value: 8, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "ObjectRayOriginNV", - Value: 5323, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Float16", + Value: 9, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "ObjectRayDirectionNV", - Value: 5324, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Float64", + Value: 10, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "RayTminNV", - Value: 5325, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Int64", + Value: 11, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "RayTmaxNV", - Value: 5326, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Int64Atomics", + Value: 12, + Capabilities: []string{"Int64",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "InstanceCustomIndexNV", - Value: 5327, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "ImageBasic", + Value: 13, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "ObjectToWorldNV", - Value: 5330, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "ImageReadWrite", + Value: 14, + Capabilities: []string{"ImageBasic",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageMipmap", + Value: 15, + Capabilities: []string{"ImageBasic",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "WorldToObjectNV", - Value: 5331, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Pipes", + Value: 17, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "HitTNV", - Value: 5332, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "Groups", + Value: 18, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "HitKindNV", - Value: 5333, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "DeviceEnqueue", + Value: 19, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "IncomingRayFlagsNV", - Value: 5351, - Capabilities: []string{"RayTracingNV",}, + Enumerant: "LiteralSampler", + Value: 20, + Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "WarpsPerSMNV", - Value: 5374, - Capabilities: []string{"ShaderSMBuiltinsNV",}, + Enumerant: "AtomicStorage", + Value: 21, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SMCountNV", - Value: 5375, - Capabilities: []string{"ShaderSMBuiltinsNV",}, + Enumerant: "Int16", + Value: 22, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "WarpIDNV", - Value: 5376, - Capabilities: []string{"ShaderSMBuiltinsNV",}, + Enumerant: "TessellationPointSize", + Value: 23, + Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SMIDNV", - Value: 5377, - Capabilities: []string{"ShaderSMBuiltinsNV",}, + Enumerant: "GeometryPointSize", + Value: 24, + Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, - }, - Bases: []*OperandKind {}, - } - OperandKindScope = &OperandKind { - Kind: "Scope", - Category: "ValueEnum", - Enumerants: []Enumerant { Enumerant{ - Enumerant: "CrossDevice", - Value: 0, - Capabilities: []string{}, + Enumerant: "ImageGatherExtended", + Value: 25, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Device", - Value: 1, - Capabilities: []string{}, + Enumerant: "StorageImageMultisample", + Value: 27, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Workgroup", - Value: 2, - Capabilities: []string{}, + Enumerant: "UniformBufferArrayDynamicIndexing", + Value: 28, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Subgroup", - Value: 3, - Capabilities: []string{}, + Enumerant: "SampledImageArrayDynamicIndexing", + Value: 29, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Invocation", - Value: 4, - Capabilities: []string{}, + Enumerant: "StorageBufferArrayDynamicIndexing", + Value: 30, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "QueueFamily", - Value: 5, - Capabilities: []string{"VulkanMemoryModel",}, + Enumerant: "StorageImageArrayDynamicIndexing", + Value: 31, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "QueueFamilyKHR", - Value: 5, - Capabilities: []string{"VulkanMemoryModel",}, + Enumerant: "ClipDistance", + Value: 32, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, - }, - Bases: []*OperandKind {}, - } - OperandKindGroupOperation = &OperandKind { - Kind: "GroupOperation", - Category: "ValueEnum", - Enumerants: []Enumerant { Enumerant{ - Enumerant: "Reduce", - Value: 0, - Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Enumerant: "CullDistance", + Value: 33, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "InclusiveScan", - Value: 1, - Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Enumerant: "ImageCubeArray", + Value: 34, + Capabilities: []string{"SampledCubeArray",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "ExclusiveScan", - Value: 2, - Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Enumerant: "SampleRateShading", + Value: 35, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "ClusteredReduce", - Value: 3, - Capabilities: []string{"GroupNonUniformClustered",}, + Enumerant: "ImageRect", + Value: 36, + Capabilities: []string{"SampledRect",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "", }, Enumerant{ - Enumerant: "PartitionedReduceNV", - Value: 6, - Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Enumerant: "SampledRect", + Value: 37, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "PartitionedInclusiveScanNV", - Value: 7, - Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Enumerant: "GenericPointer", + Value: 38, + Capabilities: []string{"Addresses",}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "PartitionedExclusiveScanNV", - Value: 8, - Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Enumerant: "Int8", + Value: 39, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, - }, - Bases: []*OperandKind {}, - } - OperandKindKernelEnqueueFlags = &OperandKind { - Kind: "KernelEnqueueFlags", - Category: "ValueEnum", - Enumerants: []Enumerant { Enumerant{ - Enumerant: "NoWait", - Value: 0, - Capabilities: []string{"Kernel",}, + Enumerant: "InputAttachment", + Value: 40, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "WaitKernel", - Value: 1, - Capabilities: []string{"Kernel",}, + Enumerant: "SparseResidency", + Value: 41, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "WaitWorkGroup", - Value: 2, - Capabilities: []string{"Kernel",}, + Enumerant: "MinLod", + Value: 42, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, - }, - Bases: []*OperandKind {}, - } - OperandKindCapability = &OperandKind { - Kind: "Capability", - Category: "ValueEnum", - Enumerants: []Enumerant { Enumerant{ - Enumerant: "Matrix", - Value: 0, + Enumerant: "Sampled1D", + Value: 43, Capabilities: []string{}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Shader", - Value: 1, - Capabilities: []string{"Matrix",}, + Enumerant: "Image1D", + Value: 44, + Capabilities: []string{"Sampled1D",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Geometry", - Value: 2, + Enumerant: "SampledCubeArray", + Value: 45, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Tessellation", - Value: 3, - Capabilities: []string{"Shader",}, + Enumerant: "SampledBuffer", + Value: 46, + Capabilities: []string{}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Addresses", - Value: 4, - Capabilities: []string{}, + Enumerant: "ImageBuffer", + Value: 47, + Capabilities: []string{"SampledBuffer",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Linkage", - Value: 5, - Capabilities: []string{}, + Enumerant: "ImageMSArray", + Value: 48, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Kernel", - Value: 6, - Capabilities: []string{}, + Enumerant: "StorageImageExtendedFormats", + Value: 49, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Vector16", - Value: 7, - Capabilities: []string{"Kernel",}, + Enumerant: "ImageQuery", + Value: 50, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Float16Buffer", - Value: 8, - Capabilities: []string{"Kernel",}, + Enumerant: "DerivativeControl", + Value: 51, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Float16", - Value: 9, - Capabilities: []string{}, + Enumerant: "InterpolationFunction", + Value: 52, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Float64", - Value: 10, - Capabilities: []string{}, + Enumerant: "TransformFeedback", + Value: 53, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Int64", - Value: 11, - Capabilities: []string{}, + Enumerant: "GeometryStreams", + Value: 54, + Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "Int64Atomics", - Value: 12, - Capabilities: []string{"Int64",}, + Enumerant: "StorageImageReadWithoutFormat", + Value: 55, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "ImageBasic", - Value: 13, - Capabilities: []string{"Kernel",}, + Enumerant: "StorageImageWriteWithoutFormat", + Value: 56, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "ImageReadWrite", - Value: 14, - Capabilities: []string{"ImageBasic",}, + Enumerant: "MultiViewport", + Value: 57, + Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ - Enumerant: "ImageMipmap", - Value: 15, - Capabilities: []string{"ImageBasic",}, + Enumerant: "SubgroupDispatch", + Value: 58, + Capabilities: []string{"DeviceEnqueue",}, Parameters: []Parameter{}, - Version: "", + Version: "1.1", }, Enumerant{ - Enumerant: "Pipes", - Value: 17, + Enumerant: "NamedBarrier", + Value: 59, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.1", }, Enumerant{ - Enumerant: "Groups", - Value: 18, - Capabilities: []string{}, + Enumerant: "PipeStorage", + Value: 60, + Capabilities: []string{"Pipes",}, Parameters: []Parameter{}, - Version: "", + Version: "1.1", }, Enumerant{ - Enumerant: "DeviceEnqueue", - Value: 19, - Capabilities: []string{"Kernel",}, + Enumerant: "GroupNonUniform", + Value: 61, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "LiteralSampler", - Value: 20, - Capabilities: []string{"Kernel",}, + Enumerant: "GroupNonUniformVote", + Value: 62, + Capabilities: []string{"GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "AtomicStorage", - Value: 21, - Capabilities: []string{"Shader",}, + Enumerant: "GroupNonUniformArithmetic", + Value: 63, + Capabilities: []string{"GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "Int16", - Value: 22, - Capabilities: []string{}, + Enumerant: "GroupNonUniformBallot", + Value: 64, + Capabilities: []string{"GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "TessellationPointSize", - Value: 23, - Capabilities: []string{"Tessellation",}, + Enumerant: "GroupNonUniformShuffle", + Value: 65, + Capabilities: []string{"GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "GeometryPointSize", - Value: 24, - Capabilities: []string{"Geometry",}, + Enumerant: "GroupNonUniformShuffleRelative", + Value: 66, + Capabilities: []string{"GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "ImageGatherExtended", - Value: 25, - Capabilities: []string{"Shader",}, + Enumerant: "GroupNonUniformClustered", + Value: 67, + Capabilities: []string{"GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "StorageImageMultisample", - Value: 27, - Capabilities: []string{"Shader",}, + Enumerant: "GroupNonUniformQuad", + Value: 68, + Capabilities: []string{"GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "UniformBufferArrayDynamicIndexing", - Value: 28, - Capabilities: []string{"Shader",}, + Enumerant: "ShaderLayer", + Value: 69, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.5", }, Enumerant{ - Enumerant: "SampledImageArrayDynamicIndexing", - Value: 29, - Capabilities: []string{"Shader",}, + Enumerant: "ShaderViewportIndex", + Value: 70, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.5", }, Enumerant{ - Enumerant: "StorageBufferArrayDynamicIndexing", - Value: 30, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupBallotKHR", + Value: 4423, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "StorageImageArrayDynamicIndexing", - Value: 31, + Enumerant: "DrawParameters", + Value: 4427, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "ClipDistance", - Value: 32, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupVoteKHR", + Value: 4431, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "CullDistance", - Value: 33, - Capabilities: []string{"Shader",}, + Enumerant: "StorageBuffer16BitAccess", + Value: 4433, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "ImageCubeArray", - Value: 34, - Capabilities: []string{"SampledCubeArray",}, + Enumerant: "StorageUniformBufferBlock16", + Value: 4433, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "SampleRateShading", - Value: 35, - Capabilities: []string{"Shader",}, + Enumerant: "UniformAndStorageBuffer16BitAccess", + Value: 4434, + Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "ImageRect", - Value: 36, - Capabilities: []string{"SampledRect",}, + Enumerant: "StorageUniform16", + Value: 4434, + Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "SampledRect", - Value: 37, - Capabilities: []string{"Shader",}, + Enumerant: "StoragePushConstant16", + Value: 4435, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "GenericPointer", - Value: 38, - Capabilities: []string{"Addresses",}, + Enumerant: "StorageInputOutput16", + Value: 4436, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "Int8", - Value: 39, + Enumerant: "DeviceGroup", + Value: 4437, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "InputAttachment", - Value: 40, + Enumerant: "MultiView", + Value: 4439, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "SparseResidency", - Value: 41, + Enumerant: "VariablePointersStorageBuffer", + Value: 4441, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "MinLod", - Value: 42, - Capabilities: []string{"Shader",}, + Enumerant: "VariablePointers", + Value: 4442, + Capabilities: []string{"VariablePointersStorageBuffer",}, Parameters: []Parameter{}, - Version: "", + Version: "1.3", }, Enumerant{ - Enumerant: "Sampled1D", - Value: 43, + Enumerant: "AtomicStorageOps", + Value: 4445, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleMaskPostDepthCoverage", + Value: 4447, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StorageBuffer8BitAccess", + Value: 4448, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.5", }, Enumerant{ - Enumerant: "Image1D", - Value: 44, - Capabilities: []string{"Sampled1D",}, + Enumerant: "UniformAndStorageBuffer8BitAccess", + Value: 4449, + Capabilities: []string{"StorageBuffer8BitAccess",}, Parameters: []Parameter{}, - Version: "", + Version: "1.5", }, Enumerant{ - Enumerant: "SampledCubeArray", - Value: 45, - Capabilities: []string{"Shader",}, + Enumerant: "StoragePushConstant8", + Value: 4450, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.5", }, Enumerant{ - Enumerant: "SampledBuffer", - Value: 46, + Enumerant: "DenormPreserve", + Value: 4464, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.4", }, Enumerant{ - Enumerant: "ImageBuffer", - Value: 47, - Capabilities: []string{"SampledBuffer",}, + Enumerant: "DenormFlushToZero", + Value: 4465, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.4", }, Enumerant{ - Enumerant: "ImageMSArray", - Value: 48, - Capabilities: []string{"Shader",}, + Enumerant: "SignedZeroInfNanPreserve", + Value: 4466, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.4", }, Enumerant{ - Enumerant: "StorageImageExtendedFormats", - Value: 49, - Capabilities: []string{"Shader",}, + Enumerant: "RoundingModeRTE", + Value: 4467, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.4", }, Enumerant{ - Enumerant: "ImageQuery", - Value: 50, - Capabilities: []string{"Shader",}, + Enumerant: "RoundingModeRTZ", + Value: 4468, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.4", }, Enumerant{ - Enumerant: "DerivativeControl", - Value: 51, + Enumerant: "Float16ImageAMD", + Value: 5008, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "InterpolationFunction", - Value: 52, + Enumerant: "ImageGatherBiasLodAMD", + Value: 5009, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "TransformFeedback", - Value: 53, + Enumerant: "FragmentMaskAMD", + Value: 5010, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "GeometryStreams", - Value: 54, - Capabilities: []string{"Geometry",}, + Enumerant: "StencilExportEXT", + Value: 5013, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "StorageImageReadWithoutFormat", - Value: 55, + Enumerant: "ImageReadWriteLodAMD", + Value: 5015, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "StorageImageWriteWithoutFormat", - Value: 56, + Enumerant: "ShaderClockKHR", + Value: 5055, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "MultiViewport", - Value: 57, - Capabilities: []string{"Geometry",}, + Enumerant: "SampleMaskOverrideCoverageNV", + Value: 5249, + Capabilities: []string{"SampleRateShading",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupDispatch", - Value: 58, - Capabilities: []string{"DeviceEnqueue",}, + Enumerant: "GeometryShaderPassthroughNV", + Value: 5251, + Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "1.1", + Version: "None", }, Enumerant{ - Enumerant: "NamedBarrier", - Value: 59, - Capabilities: []string{"Kernel",}, + Enumerant: "ShaderViewportIndexLayerEXT", + Value: 5254, + Capabilities: []string{"MultiViewport",}, Parameters: []Parameter{}, - Version: "1.1", + Version: "None", }, Enumerant{ - Enumerant: "PipeStorage", - Value: 60, - Capabilities: []string{"Pipes",}, + Enumerant: "ShaderViewportIndexLayerNV", + Value: 5254, + Capabilities: []string{"MultiViewport",}, Parameters: []Parameter{}, - Version: "1.1", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniform", - Value: 61, - Capabilities: []string{}, + Enumerant: "ShaderViewportMaskNV", + Value: 5255, + Capabilities: []string{"ShaderViewportIndexLayerNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformVote", - Value: 62, - Capabilities: []string{"GroupNonUniform",}, + Enumerant: "ShaderStereoViewNV", + Value: 5259, + Capabilities: []string{"ShaderViewportMaskNV",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformArithmetic", - Value: 63, - Capabilities: []string{"GroupNonUniform",}, + Enumerant: "PerViewAttributesNV", + Value: 5260, + Capabilities: []string{"MultiView",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformBallot", - Value: 64, - Capabilities: []string{"GroupNonUniform",}, + Enumerant: "FragmentFullyCoveredEXT", + Value: 5265, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformShuffle", - Value: 65, - Capabilities: []string{"GroupNonUniform",}, + Enumerant: "MeshShadingNV", + Value: 5266, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformShuffleRelative", - Value: 66, - Capabilities: []string{"GroupNonUniform",}, + Enumerant: "ImageFootprintNV", + Value: 5282, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformClustered", - Value: 67, - Capabilities: []string{"GroupNonUniform",}, + Enumerant: "FragmentBarycentricNV", + Value: 5284, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformQuad", - Value: 68, - Capabilities: []string{"GroupNonUniform",}, + Enumerant: "ComputeDerivativeGroupQuadsNV", + Value: 5288, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.3", + Version: "None", }, Enumerant{ - Enumerant: "ShaderLayer", - Value: 69, - Capabilities: []string{}, + Enumerant: "FragmentDensityEXT", + Value: 5291, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", }, Enumerant{ - Enumerant: "ShaderViewportIndex", - Value: 70, - Capabilities: []string{}, + Enumerant: "ShadingRateNV", + Value: 5291, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", }, Enumerant{ - Enumerant: "SubgroupBallotKHR", - Value: 4423, + Enumerant: "GroupNonUniformPartitionedNV", + Value: 5297, Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "DrawParameters", - Value: 4427, + Enumerant: "ShaderNonUniform", + Value: 5301, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "SubgroupVoteKHR", - Value: 4431, - Capabilities: []string{}, + Enumerant: "ShaderNonUniformEXT", + Value: 5301, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "StorageBuffer16BitAccess", - Value: 4433, - Capabilities: []string{}, + Enumerant: "RuntimeDescriptorArray", + Value: 5302, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "StorageUniformBufferBlock16", - Value: 4433, - Capabilities: []string{}, + Enumerant: "RuntimeDescriptorArrayEXT", + Value: 5302, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "UniformAndStorageBuffer16BitAccess", - Value: 4434, - Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, + Enumerant: "InputAttachmentArrayDynamicIndexing", + Value: 5303, + Capabilities: []string{"InputAttachment",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "StorageUniform16", - Value: 4434, - Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, + Enumerant: "InputAttachmentArrayDynamicIndexingEXT", + Value: 5303, + Capabilities: []string{"InputAttachment",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "StoragePushConstant16", - Value: 4435, - Capabilities: []string{}, + Enumerant: "UniformTexelBufferArrayDynamicIndexing", + Value: 5304, + Capabilities: []string{"SampledBuffer",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "StorageInputOutput16", - Value: 4436, - Capabilities: []string{}, + Enumerant: "UniformTexelBufferArrayDynamicIndexingEXT", + Value: 5304, + Capabilities: []string{"SampledBuffer",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "DeviceGroup", - Value: 4437, - Capabilities: []string{}, + Enumerant: "StorageTexelBufferArrayDynamicIndexing", + Value: 5305, + Capabilities: []string{"ImageBuffer",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "MultiView", - Value: 4439, - Capabilities: []string{"Shader",}, + Enumerant: "StorageTexelBufferArrayDynamicIndexingEXT", + Value: 5305, + Capabilities: []string{"ImageBuffer",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "VariablePointersStorageBuffer", - Value: 4441, - Capabilities: []string{"Shader",}, + Enumerant: "UniformBufferArrayNonUniformIndexing", + Value: 5306, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "VariablePointers", - Value: 4442, - Capabilities: []string{"VariablePointersStorageBuffer",}, + Enumerant: "UniformBufferArrayNonUniformIndexingEXT", + Value: 5306, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "1.3", + Version: "1.5", }, Enumerant{ - Enumerant: "AtomicStorageOps", - Value: 4445, - Capabilities: []string{}, + Enumerant: "SampledImageArrayNonUniformIndexing", + Value: 5307, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "SampleMaskPostDepthCoverage", - Value: 4447, - Capabilities: []string{}, + Enumerant: "SampledImageArrayNonUniformIndexingEXT", + Value: 5307, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "StorageBuffer8BitAccess", - Value: 4448, - Capabilities: []string{}, + Enumerant: "StorageBufferArrayNonUniformIndexing", + Value: 5308, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, Version: "1.5", }, Enumerant{ - Enumerant: "UniformAndStorageBuffer8BitAccess", - Value: 4449, - Capabilities: []string{"StorageBuffer8BitAccess",}, + Enumerant: "StorageBufferArrayNonUniformIndexingEXT", + Value: 5308, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, Version: "1.5", }, Enumerant{ - Enumerant: "StoragePushConstant8", - Value: 4450, - Capabilities: []string{}, + Enumerant: "StorageImageArrayNonUniformIndexing", + Value: 5309, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, Version: "1.5", }, Enumerant{ - Enumerant: "DenormPreserve", - Value: 4464, - Capabilities: []string{}, + Enumerant: "StorageImageArrayNonUniformIndexingEXT", + Value: 5309, + Capabilities: []string{"ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "1.4", + Version: "1.5", }, Enumerant{ - Enumerant: "DenormFlushToZero", - Value: 4465, - Capabilities: []string{}, + Enumerant: "InputAttachmentArrayNonUniformIndexing", + Value: 5310, + Capabilities: []string{"InputAttachment","ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "1.4", + Version: "1.5", }, Enumerant{ - Enumerant: "SignedZeroInfNanPreserve", - Value: 4466, - Capabilities: []string{}, + Enumerant: "InputAttachmentArrayNonUniformIndexingEXT", + Value: 5310, + Capabilities: []string{"InputAttachment","ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "1.4", + Version: "1.5", }, Enumerant{ - Enumerant: "RoundingModeRTE", - Value: 4467, - Capabilities: []string{}, + Enumerant: "UniformTexelBufferArrayNonUniformIndexing", + Value: 5311, + Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "1.4", + Version: "1.5", }, Enumerant{ - Enumerant: "RoundingModeRTZ", - Value: 4468, - Capabilities: []string{}, + Enumerant: "UniformTexelBufferArrayNonUniformIndexingEXT", + Value: 5311, + Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "1.4", + Version: "1.5", }, Enumerant{ - Enumerant: "Float16ImageAMD", - Value: 5008, - Capabilities: []string{"Shader",}, + Enumerant: "StorageTexelBufferArrayNonUniformIndexing", + Value: 5312, + Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "ImageGatherBiasLodAMD", - Value: 5009, - Capabilities: []string{"Shader",}, + Enumerant: "StorageTexelBufferArrayNonUniformIndexingEXT", + Value: 5312, + Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "FragmentMaskAMD", - Value: 5010, + Enumerant: "RayTracingNV", + Value: 5340, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "StencilExportEXT", - Value: 5013, - Capabilities: []string{"Shader",}, + Enumerant: "VulkanMemoryModel", + Value: 5345, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "ImageReadWriteLodAMD", - Value: 5015, - Capabilities: []string{"Shader",}, + Enumerant: "VulkanMemoryModelKHR", + Value: 5345, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "ShaderClockKHR", - Value: 5055, - Capabilities: []string{"Shader",}, + Enumerant: "VulkanMemoryModelDeviceScope", + Value: 5346, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "SampleMaskOverrideCoverageNV", - Value: 5249, - Capabilities: []string{"SampleRateShading",}, + Enumerant: "VulkanMemoryModelDeviceScopeKHR", + Value: 5346, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "GeometryShaderPassthroughNV", - Value: 5251, - Capabilities: []string{"Geometry",}, + Enumerant: "PhysicalStorageBufferAddresses", + Value: 5347, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "ShaderViewportIndexLayerEXT", - Value: 5254, - Capabilities: []string{"MultiViewport",}, + Enumerant: "PhysicalStorageBufferAddressesEXT", + Value: 5347, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "None", + Version: "1.5", }, Enumerant{ - Enumerant: "ShaderViewportIndexLayerNV", - Value: 5254, - Capabilities: []string{"MultiViewport",}, + Enumerant: "ComputeDerivativeGroupLinearNV", + Value: 5350, + Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ShaderViewportMaskNV", - Value: 5255, - Capabilities: []string{"ShaderViewportIndexLayerNV",}, + Enumerant: "CooperativeMatrixNV", + Value: 5357, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ShaderStereoViewNV", - Value: 5259, - Capabilities: []string{"ShaderViewportMaskNV",}, + Enumerant: "FragmentShaderSampleInterlockEXT", + Value: 5363, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "PerViewAttributesNV", - Value: 5260, - Capabilities: []string{"MultiView",}, + Enumerant: "FragmentShaderShadingRateInterlockEXT", + Value: 5372, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "FragmentFullyCoveredEXT", - Value: 5265, + Enumerant: "ShaderSMBuiltinsNV", + Value: 5373, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "MeshShadingNV", - Value: 5266, + Enumerant: "FragmentShaderPixelInterlockEXT", + Value: 5378, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ImageFootprintNV", - Value: 5282, - Capabilities: []string{}, + Enumerant: "DemoteToHelperInvocationEXT", + Value: 5379, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "FragmentBarycentricNV", - Value: 5284, + Enumerant: "SubgroupShuffleINTEL", + Value: 5568, Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ComputeDerivativeGroupQuadsNV", - Value: 5288, + Enumerant: "SubgroupBufferBlockIOINTEL", + Value: 5569, Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "FragmentDensityEXT", - Value: 5291, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupImageBlockIOINTEL", + Value: 5570, + Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ShadingRateNV", - Value: 5291, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupImageMediaBlockIOINTEL", + Value: 5579, + Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "GroupNonUniformPartitionedNV", - Value: 5297, - Capabilities: []string{}, + Enumerant: "IntegerFunctions2INTEL", + Value: 5584, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ShaderNonUniform", - Value: 5301, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupAvcMotionEstimationINTEL", + Value: 5696, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", }, Enumerant{ - Enumerant: "ShaderNonUniformEXT", - Value: 5301, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupAvcMotionEstimationIntraINTEL", + Value: 5697, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", }, Enumerant{ - Enumerant: "RuntimeDescriptorArray", - Value: 5302, - Capabilities: []string{"Shader",}, + Enumerant: "SubgroupAvcMotionEstimationChromaINTEL", + Value: 5698, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", }, + }, + Bases: []*OperandKind {}, + } + OperandKindIdResultType = &OperandKind { + Kind: "IdResultType", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdResult = &OperandKind { + Kind: "IdResult", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdMemorySemantics = &OperandKind { + Kind: "IdMemorySemantics", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdScope = &OperandKind { + Kind: "IdScope", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdRef = &OperandKind { + Kind: "IdRef", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralInteger = &OperandKind { + Kind: "LiteralInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralString = &OperandKind { + Kind: "LiteralString", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralContextDependentNumber = &OperandKind { + Kind: "LiteralContextDependentNumber", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralExtInstInteger = &OperandKind { + Kind: "LiteralExtInstInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralSpecConstantOpInteger = &OperandKind { + Kind: "LiteralSpecConstantOpInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindPairLiteralIntegerIdRef = &OperandKind { + Kind: "PairLiteralIntegerIdRef", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindLiteralInteger,OperandKindIdRef,}, + } + OperandKindPairIdRefLiteralInteger = &OperandKind { + Kind: "PairIdRefLiteralInteger", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindIdRef,OperandKindLiteralInteger,}, + } + OperandKindPairIdRefIdRef = &OperandKind { + Kind: "PairIdRefIdRef", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindIdRef,OperandKindIdRef,}, + } + OperandKindDebugInfoFlags = &OperandKind { + Kind: "DebugInfoFlags", + Category: "BitEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "RuntimeDescriptorArrayEXT", - Value: 5302, - Capabilities: []string{"Shader",}, + Enumerant: "FlagIsProtected", + Value: 0x01, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "InputAttachmentArrayDynamicIndexing", - Value: 5303, - Capabilities: []string{"InputAttachment",}, + Enumerant: "FlagIsPrivate", + Value: 0x02, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "InputAttachmentArrayDynamicIndexingEXT", - Value: 5303, - Capabilities: []string{"InputAttachment",}, + Enumerant: "FlagIsPublic", + Value: 0x03, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "UniformTexelBufferArrayDynamicIndexing", - Value: 5304, - Capabilities: []string{"SampledBuffer",}, + Enumerant: "FlagIsLocal", + Value: 0x04, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "UniformTexelBufferArrayDynamicIndexingEXT", - Value: 5304, - Capabilities: []string{"SampledBuffer",}, + Enumerant: "FlagIsDefinition", + Value: 0x08, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageTexelBufferArrayDynamicIndexing", - Value: 5305, - Capabilities: []string{"ImageBuffer",}, + Enumerant: "FlagFwdDecl", + Value: 0x10, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageTexelBufferArrayDynamicIndexingEXT", - Value: 5305, - Capabilities: []string{"ImageBuffer",}, + Enumerant: "FlagArtificial", + Value: 0x20, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "UniformBufferArrayNonUniformIndexing", - Value: 5306, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagExplicit", + Value: 0x40, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "UniformBufferArrayNonUniformIndexingEXT", - Value: 5306, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagPrototyped", + Value: 0x80, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "SampledImageArrayNonUniformIndexing", - Value: 5307, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagObjectPointer", + Value: 0x100, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "SampledImageArrayNonUniformIndexingEXT", - Value: 5307, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagStaticMember", + Value: 0x200, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageBufferArrayNonUniformIndexing", - Value: 5308, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagIndirectVariable", + Value: 0x400, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageBufferArrayNonUniformIndexingEXT", - Value: 5308, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagLValueReference", + Value: 0x800, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageImageArrayNonUniformIndexing", - Value: 5309, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagRValueReference", + Value: 0x1000, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageImageArrayNonUniformIndexingEXT", - Value: 5309, - Capabilities: []string{"ShaderNonUniform",}, + Enumerant: "FlagIsOptimized", + Value: 0x2000, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "InputAttachmentArrayNonUniformIndexing", - Value: 5310, - Capabilities: []string{"InputAttachment","ShaderNonUniform",}, + Enumerant: "FlagIsEnumClass", + Value: 0x4000, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "InputAttachmentArrayNonUniformIndexingEXT", - Value: 5310, - Capabilities: []string{"InputAttachment","ShaderNonUniform",}, + Enumerant: "FlagTypePassByValue", + Value: 0x8000, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "UniformTexelBufferArrayNonUniformIndexing", - Value: 5311, - Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, + Enumerant: "FlagTypePassByReference", + Value: 0x10000, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugBaseTypeAttributeEncoding = &OperandKind { + Kind: "DebugBaseTypeAttributeEncoding", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "UniformTexelBufferArrayNonUniformIndexingEXT", - Value: 5311, - Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, + Enumerant: "Unspecified", + Value: 0, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageTexelBufferArrayNonUniformIndexing", - Value: 5312, - Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, + Enumerant: "Address", + Value: 1, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "StorageTexelBufferArrayNonUniformIndexingEXT", - Value: 5312, - Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, + Enumerant: "Boolean", + Value: 2, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "RayTracingNV", - Value: 5340, - Capabilities: []string{"Shader",}, + Enumerant: "Float", + Value: 3, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "VulkanMemoryModel", - Value: 5345, + Enumerant: "Signed", + Value: 4, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "VulkanMemoryModelKHR", - Value: 5345, + Enumerant: "SignedChar", + Value: 5, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "VulkanMemoryModelDeviceScope", - Value: 5346, + Enumerant: "Unsigned", + Value: 6, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "VulkanMemoryModelDeviceScopeKHR", - Value: 5346, + Enumerant: "UnsignedChar", + Value: 7, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugCompositeType = &OperandKind { + Kind: "DebugCompositeType", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "PhysicalStorageBufferAddresses", - Value: 5347, - Capabilities: []string{"Shader",}, + Enumerant: "Class", + Value: 0, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "PhysicalStorageBufferAddressesEXT", - Value: 5347, - Capabilities: []string{"Shader",}, + Enumerant: "Structure", + Value: 1, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "1.5", + Version: "", }, Enumerant{ - Enumerant: "ComputeDerivativeGroupLinearNV", - Value: 5350, + Enumerant: "Union", + Value: 2, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugTypeQualifier = &OperandKind { + Kind: "DebugTypeQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "CooperativeMatrixNV", - Value: 5357, - Capabilities: []string{"Shader",}, + Enumerant: "ConstType", + Value: 0, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "FragmentShaderSampleInterlockEXT", - Value: 5363, - Capabilities: []string{"Shader",}, + Enumerant: "VolatileType", + Value: 1, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "FragmentShaderShadingRateInterlockEXT", - Value: 5372, - Capabilities: []string{"Shader",}, + Enumerant: "RestrictType", + Value: 2, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "ShaderSMBuiltinsNV", - Value: 5373, - Capabilities: []string{"Shader",}, + Enumerant: "AtomicType", + Value: 3, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugOperation = &OperandKind { + Kind: "DebugOperation", + Category: "ValueEnum", + Enumerants: []Enumerant { Enumerant{ - Enumerant: "FragmentShaderPixelInterlockEXT", - Value: 5378, - Capabilities: []string{"Shader",}, + Enumerant: "Deref", + Value: 0, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "DemoteToHelperInvocationEXT", - Value: 5379, - Capabilities: []string{"Shader",}, + Enumerant: "Plus", + Value: 1, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SubgroupShuffleINTEL", - Value: 5568, + Enumerant: "Minus", + Value: 2, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SubgroupBufferBlockIOINTEL", - Value: 5569, + Enumerant: "PlusUconst", + Value: 3, Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "", }, Enumerant{ - Enumerant: "SubgroupImageBlockIOINTEL", - Value: 5570, + Enumerant: "BitPiece", + Value: 4, Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", + Parameters: []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},}, + Version: "", }, Enumerant{ - Enumerant: "SubgroupImageMediaBlockIOINTEL", - Value: 5579, + Enumerant: "Swap", + Value: 5, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "IntegerFunctions2INTEL", - Value: 5584, - Capabilities: []string{"Shader",}, + Enumerant: "Xderef", + Value: 6, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SubgroupAvcMotionEstimationINTEL", - Value: 5696, + Enumerant: "StackValue", + Value: 7, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "None", + Version: "", }, Enumerant{ - Enumerant: "SubgroupAvcMotionEstimationIntraINTEL", - Value: 5697, + Enumerant: "Constu", + Value: 8, Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "", }, Enumerant{ - Enumerant: "SubgroupAvcMotionEstimationChromaINTEL", - Value: 5698, + Enumerant: "Fragment", + Value: 9, Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", + Parameters: []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},}, + Version: "", }, }, Bases: []*OperandKind {}, } - OperandKindIdResultType = &OperandKind { - Kind: "IdResultType", - Category: "Id", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindIdResult = &OperandKind { - Kind: "IdResult", - Category: "Id", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindIdMemorySemantics = &OperandKind { - Kind: "IdMemorySemantics", - Category: "Id", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindIdScope = &OperandKind { - Kind: "IdScope", - Category: "Id", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindIdRef = &OperandKind { - Kind: "IdRef", - Category: "Id", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindLiteralInteger = &OperandKind { - Kind: "LiteralInteger", - Category: "Literal", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindLiteralString = &OperandKind { - Kind: "LiteralString", - Category: "Literal", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindLiteralContextDependentNumber = &OperandKind { - Kind: "LiteralContextDependentNumber", - Category: "Literal", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindLiteralExtInstInteger = &OperandKind { - Kind: "LiteralExtInstInteger", - Category: "Literal", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {}, - } - OperandKindLiteralSpecConstantOpInteger = &OperandKind { - Kind: "LiteralSpecConstantOpInteger", - Category: "Literal", + OperandKindDebugImportedEntity = &OperandKind { + Kind: "DebugImportedEntity", + Category: "ValueEnum", Enumerants: []Enumerant { + Enumerant{ + Enumerant: "ImportedModule", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImportedDeclaration", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, }, Bases: []*OperandKind {}, } - OperandKindPairLiteralIntegerIdRef = &OperandKind { - Kind: "PairLiteralIntegerIdRef", - Category: "Composite", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {OperandKindLiteralInteger,OperandKindIdRef,}, - } - OperandKindPairIdRefLiteralInteger = &OperandKind { - Kind: "PairIdRefLiteralInteger", - Category: "Composite", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {OperandKindIdRef,OperandKindLiteralInteger,}, - } - OperandKindPairIdRefIdRef = &OperandKind { - Kind: "PairIdRefIdRef", - Category: "Composite", - Enumerants: []Enumerant { - }, - Bases: []*OperandKind {OperandKindIdRef,OperandKindIdRef,}, - } ) diff --git a/utils/vscode/src/schema/schema.go.tmpl b/utils/vscode/src/schema/schema.go.tmpl index b584058763..ce5500fe8a 100644 --- a/utils/vscode/src/schema/schema.go.tmpl +++ b/utils/vscode/src/schema/schema.go.tmpl @@ -97,13 +97,23 @@ const ( OperandCategoryComposite = "Composite" ) +// OpcodeMap is a map of opcode name to Opcode type. +type OpcodeMap map[string]*Opcode + var ( // Opcodes is a map of opcode name to Opcode description. - Opcodes = map[string]*Opcode {•{{range $i := .Instructions}} - "{{$i.Opname}}": {{$i.Opname}},{{end}} + Opcodes = OpcodeMap {•{{range $i := .SPIRV.Instructions}} + "{{$i.Opname}}": {{Title $i.Opname}},{{end}} + } + + // ExtOpcodes is a map of extension name to Opcode description list. + ExtOpcodes = map[string]OpcodeMap {•{{range $ext := .Extensions}} + "{{$ext.Name}}": {•{{range $i := $ext.Instructions}} + "{{$i.Opname}}": {{Global $ext.Name}}_{{$i.Opname}},{{end}} + },{{end}} } -{{range $i := .Instructions}} {{$i.Opname}} = &Opcode { +{{range $i := .SPIRV.Instructions}} {{Title $i.Opname}} = &Opcode { Opname: "{{$i.Opname}}", Operands: []Operand {•{{range $i := $i.Operands}} Operand { @@ -114,8 +124,19 @@ var ( }, } {{end}} +{{range $ext := .Extensions}}{{range $i := $ext.Instructions}} {{Global $ext.Name}}_{{$i.Opname}} = &Opcode { + Opname: "{{$i.Opname}}", + Operands: []Operand {•{{range $i := $i.Operands}} + Operand { + Kind: OperandKind{{$i.Kind}}, + Name: "{{Replace $i.Name "\n" " "}}", + Quantifier: "{{$i.Quantifier}}", + }, {{end}} + }, + } +{{end}}{{end}} -{{range $o := .OperandKinds}} OperandKind{{$o.Kind}} = &OperandKind { +{{range $o := .All.OperandKinds}} OperandKind{{$o.Kind}} = &OperandKind { Kind: "{{$o.Kind}}", Category: "{{$o.Category}}", Enumerants: []Enumerant {•{{range $e := $o.Enumerants}} diff --git a/utils/vscode/src/tools/gen-grammar.go b/utils/vscode/src/tools/gen-grammar.go index 42cbbe9aca..200f695949 100644 --- a/utils/vscode/src/tools/gen-grammar.go +++ b/utils/vscode/src/tools/gen-grammar.go @@ -34,12 +34,30 @@ import ( "../grammar" ) -const ( - spirvGrammarURL = "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json" - spirvGrammarName = "spirv.core.grammar.json" -) +type grammarDefinition struct { + name string + url string +} var ( + spirvGrammar = grammarDefinition{ + name: "SPIR-V", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json", + } + + extensionGrammars = []grammarDefinition{ + { + name: "GLSL.std.450", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.glsl.std.450.grammar.json", + }, { + name: "OpenCL.std", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.std.100.grammar.json", + }, { + name: "OpenCL.DebugInfo.100", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Tools/master/source/extinst.opencl.debuginfo.100.grammar.json", + }, + } + templatePath = flag.String("template", "", "Path to input template file (required)") outputPath = flag.String("out", "", "Path to output generated file (required)") cachePath = flag.String("cache", "", "Cache directory for downloaded files (optional)") @@ -67,6 +85,34 @@ func run() error { if err != nil { return errors.Wrap(err, "Could not open template file") } + + type extension struct { + grammar.Root + Name string + } + + args := struct { + SPIRV grammar.Root + Extensions []extension + All grammar.Root // Combination of SPIRV + Extensions + }{} + + if args.SPIRV, err = parseGrammar(spirvGrammar); err != nil { + return errors.Wrap(err, "Failed to parse SPIR-V grammar file") + } + args.All.Instructions = append(args.All.Instructions, args.SPIRV.Instructions...) + args.All.OperandKinds = append(args.All.OperandKinds, args.SPIRV.OperandKinds...) + + for _, ext := range extensionGrammars { + root, err := parseGrammar(ext) + if err != nil { + return errors.Wrap(err, "Failed to parse extension grammar file") + } + args.Extensions = append(args.Extensions, extension{Root: root, Name: ext.name}) + args.All.Instructions = append(args.All.Instructions, root.Instructions...) + args.All.OperandKinds = append(args.All.OperandKinds, root.OperandKinds...) + } + t, err := template.New("tmpl"). Funcs(template.FuncMap{ "GenerateArguments": func() string { @@ -96,24 +142,30 @@ func run() error { } return sb.String() }, + "AllExtOpcodes": func() string { + sb := strings.Builder{} + for _, ext := range args.Extensions { + for _, inst := range ext.Root.Instructions { + if sb.Len() > 0 { + sb.WriteString("|") + } + sb.WriteString(inst.Opname) + } + } + return sb.String() + }, + "Title": strings.Title, "Replace": strings.ReplaceAll, + "Global": func(s string) string { + return strings.ReplaceAll(strings.Title(s), ".", "") + }, }).Parse(string(tf)) if err != nil { return errors.Wrap(err, "Failed to parse template") } - file, err := getOrDownload(spirvGrammarName, spirvGrammarURL) - if err != nil { - return errors.Wrap(err, "Failed to load grammar file") - } - - g := grammar.Root{} - if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil { - return errors.Wrap(err, "Failed to parse grammar file") - } - buf := bytes.Buffer{} - if err := t.Execute(&buf, g); err != nil { + if err := t.Execute(&buf, args); err != nil { return errors.Wrap(err, "Failed to execute template") } @@ -127,6 +179,22 @@ func run() error { return nil } +// parseGrammar downloads (or loads from the cache) the grammar file and returns +// the parsed grammar.Root. +func parseGrammar(def grammarDefinition) (grammar.Root, error) { + file, err := getOrDownload(def.name, def.url) + if err != nil { + return grammar.Root{}, errors.Wrap(err, "Failed to load grammar file") + } + + g := grammar.Root{} + if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil { + return grammar.Root{}, errors.Wrap(err, "Failed to parse grammar file") + } + + return g, nil +} + // getOrDownload loads the specific file from the cache, or downloads the file // from the given url. func getOrDownload(name, url string) ([]byte, error) { From 1f03ac10270a7ba5cd1b5b1969cab298503598c8 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 5 Feb 2020 21:07:44 +0000 Subject: [PATCH 16/88] spirv-fuzz: Fuzzer passes to add local and global variables (#3175) Adds two new fuzzer passes to add variables to a module: one that adds Private storage class global variables, another that adds Function storage class local variables. --- source/fuzz/CMakeLists.txt | 6 + source/fuzz/fuzzer.cpp | 8 + source/fuzz/fuzzer_context.cpp | 6 + source/fuzz/fuzzer_context.h | 8 + source/fuzz/fuzzer_pass.cpp | 181 +++++++++++++++ source/fuzz/fuzzer_pass.h | 73 +++++++ .../fuzz/fuzzer_pass_add_global_variables.cpp | 75 +++++++ .../fuzz/fuzzer_pass_add_global_variables.h | 40 ++++ .../fuzz/fuzzer_pass_add_local_variables.cpp | 79 +++++++ source/fuzz/fuzzer_pass_add_local_variables.h | 43 ++++ source/fuzz/fuzzer_util.cpp | 9 + source/fuzz/fuzzer_util.h | 4 + source/fuzz/protobufs/spvtoolsfuzz.proto | 34 ++- source/fuzz/transformation.cpp | 4 + .../transformation_add_local_variable.cpp | 98 +++++++++ .../fuzz/transformation_add_local_variable.h | 60 +++++ test/fuzz/CMakeLists.txt | 1 + ...transformation_add_local_variable_test.cpp | 206 ++++++++++++++++++ 18 files changed, 930 insertions(+), 5 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_add_global_variables.cpp create mode 100644 source/fuzz/fuzzer_pass_add_global_variables.h create mode 100644 source/fuzz/fuzzer_pass_add_local_variables.cpp create mode 100644 source/fuzz/fuzzer_pass_add_local_variables.h create mode 100644 source/fuzz/transformation_add_local_variable.cpp create mode 100644 source/fuzz/transformation_add_local_variable.h create mode 100644 test/fuzz/transformation_add_local_variable_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index c816f87576..b3c197034a 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -40,6 +40,8 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_blocks.h fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_dead_continues.h + fuzzer_pass_add_global_variables.h + fuzzer_pass_add_local_variables.h fuzzer_pass_add_no_contraction_decorations.h fuzzer_pass_add_useful_constructs.h fuzzer_pass_adjust_function_controls.h @@ -74,6 +76,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_function.h transformation_add_global_undef.h transformation_add_global_variable.h + transformation_add_local_variable.h transformation_add_no_contraction_decoration.h transformation_add_type_array.h transformation_add_type_boolean.h @@ -112,6 +115,8 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_blocks.cpp fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_dead_continues.cpp + fuzzer_pass_add_global_variables.cpp + fuzzer_pass_add_local_variables.cpp fuzzer_pass_add_no_contraction_decorations.cpp fuzzer_pass_add_useful_constructs.cpp fuzzer_pass_adjust_function_controls.cpp @@ -145,6 +150,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_function.cpp transformation_add_global_undef.cpp transformation_add_global_variable.cpp + transformation_add_local_variable.cpp transformation_add_no_contraction_decoration.cpp transformation_add_type_array.cpp transformation_add_type_boolean.cpp diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 27b697c947..8caa853037 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -25,6 +25,8 @@ #include "source/fuzz/fuzzer_pass_add_dead_blocks.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_global_variables.h" +#include "source/fuzz/fuzzer_pass_add_local_variables.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" @@ -191,6 +193,12 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 916803a7bb..0fb275851e 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -29,6 +29,8 @@ const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; const std::pair kChanceOfAddingDeadBlock = {20, 90}; const std::pair kChanceOfAddingDeadBreak = {5, 80}; const std::pair kChanceOfAddingDeadContinue = {5, 80}; +const std::pair kChanceOfAddingGlobalVariable = {20, 90}; +const std::pair kChanceOfAddingLocalVariable = {20, 90}; const std::pair kChanceOfAddingMatrixType = {20, 70}; const std::pair kChanceOfAddingNoContractionDecoration = { 5, 70}; @@ -88,6 +90,10 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak); chance_of_adding_dead_continue_ = ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue); + chance_of_adding_global_variable_ = + ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable); + chance_of_adding_local_variable_ = + ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable); chance_of_adding_matrix_type_ = ChooseBetweenMinAndMax(kChanceOfAddingMatrixType); chance_of_adding_no_contraction_decoration_ = diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index d4d6d58fbb..2c48ac5a15 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -69,6 +69,12 @@ class FuzzerContext { uint32_t GetChanceOfAddingDeadContinue() { return chance_of_adding_dead_continue_; } + uint32_t GetChanceOfAddingGlobalVariable() { + return chance_of_adding_global_variable_; + } + uint32_t GetChanceOfAddingLocalVariable() { + return chance_of_adding_local_variable_; + } uint32_t GetChanceOfAddingMatrixType() { return chance_of_adding_matrix_type_; } @@ -148,6 +154,8 @@ class FuzzerContext { uint32_t chance_of_adding_dead_block_; uint32_t chance_of_adding_dead_break_; uint32_t chance_of_adding_dead_continue_; + uint32_t chance_of_adding_global_variable_; + uint32_t chance_of_adding_local_variable_; uint32_t chance_of_adding_matrix_type_; uint32_t chance_of_adding_no_contraction_decoration_; uint32_t chance_of_adding_vector_type_; diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 9964a6c3f5..40eb3bd395 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -14,7 +14,10 @@ #include "source/fuzz/fuzzer_pass.h" +#include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" #include "source/fuzz/transformation_add_constant_scalar.h" #include "source/fuzz/transformation_add_global_undef.h" #include "source/fuzz/transformation_add_type_boolean.h" @@ -243,6 +246,42 @@ uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word, return result; } +uint32_t FuzzerPass::FindOrCreate32BitFloatConstant(uint32_t word) { + auto float_type_id = FindOrCreate32BitFloatType(); + opt::analysis::FloatConstant float_constant( + GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(), + {word}); + auto existing_constant = + GetIRContext()->get_constant_mgr()->FindConstant(&float_constant); + if (existing_constant) { + return GetIRContext() + ->get_constant_mgr() + ->GetDefiningInstruction(existing_constant) + ->result_id(); + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddConstantScalar(result, float_type_id, {word})); + return result; +} + +uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) { + auto bool_type_id = FindOrCreateBoolType(); + opt::analysis::BoolConstant bool_constant( + GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value); + auto existing_constant = + GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant); + if (existing_constant) { + return GetIRContext() + ->get_constant_mgr() + ->GetDefiningInstruction(existing_constant) + ->result_id(); + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantBoolean(result, value)); + return result; +} + uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { for (auto& inst : GetIRContext()->types_values()) { if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) { @@ -254,5 +293,147 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { return result; } +std::pair, std::map>> +FuzzerPass::GetAvailableBaseTypesAndPointers( + SpvStorageClass storage_class) const { + // Records all of the base types available in the module. + std::vector base_types; + + // For each base type, records all the associated pointer types that target + // that base type and that have |storage_class| as their storage class. + std::map> base_type_to_pointers; + + for (auto& inst : GetIRContext()->types_values()) { + switch (inst.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeStruct: + case SpvOpTypeVector: + // These types are suitable as pointer base types. Record the type, + // and the fact that we cannot yet have seen any pointers that use this + // as its base type. + base_types.push_back(inst.result_id()); + base_type_to_pointers.insert({inst.result_id(), {}}); + break; + case SpvOpTypePointer: + if (inst.GetSingleWordInOperand(0) == storage_class) { + // The pointer has the desired storage class, so we are interested in + // it. Associate it with its base type. + base_type_to_pointers.at(inst.GetSingleWordInOperand(1)) + .push_back(inst.result_id()); + } + break; + default: + break; + } + } + return {base_types, base_type_to_pointers}; +} + +uint32_t FuzzerPass::FindOrCreateZeroConstant( + uint32_t scalar_or_composite_type_id) { + auto type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id); + assert(type_instruction && "The type instruction must exist."); + switch (type_instruction->opcode()) { + case SpvOpTypeBool: + return FindOrCreateBoolConstant(false); + case SpvOpTypeFloat: + return FindOrCreate32BitFloatConstant(0); + case SpvOpTypeInt: + return FindOrCreate32BitIntegerConstant( + 0, type_instruction->GetSingleWordInOperand(1) != 0); + case SpvOpTypeArray: { + return GetZeroConstantForHomogeneousComposite( + *type_instruction, type_instruction->GetSingleWordInOperand(0), + fuzzerutil::GetArraySize(*type_instruction, GetIRContext())); + } + case SpvOpTypeMatrix: + case SpvOpTypeVector: { + return GetZeroConstantForHomogeneousComposite( + *type_instruction, type_instruction->GetSingleWordInOperand(0), + type_instruction->GetSingleWordInOperand(1)); + } + case SpvOpTypeStruct: { + std::vector field_zero_constants; + std::vector field_zero_ids; + for (uint32_t index = 0; index < type_instruction->NumInOperands(); + index++) { + uint32_t field_constant_id = FindOrCreateZeroConstant( + type_instruction->GetSingleWordInOperand(index)); + field_zero_ids.push_back(field_constant_id); + field_zero_constants.push_back( + GetIRContext()->get_constant_mgr()->FindDeclaredConstant( + field_constant_id)); + } + return FindOrCreateCompositeConstant( + *type_instruction, field_zero_constants, field_zero_ids); + } + default: + assert(false && "Unknown type."); + return 0; + } +} + +uint32_t FuzzerPass::FindOrCreateCompositeConstant( + const opt::Instruction& composite_type_instruction, + const std::vector& constants, + const std::vector& constant_ids) { + assert(constants.size() == constant_ids.size() && + "Precondition: |constants| and |constant_ids| must be in " + "correspondence."); + + opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType( + composite_type_instruction.result_id()); + std::unique_ptr composite_constant; + if (composite_type->AsArray()) { + composite_constant = MakeUnique( + composite_type->AsArray(), constants); + } else if (composite_type->AsMatrix()) { + composite_constant = MakeUnique( + composite_type->AsMatrix(), constants); + } else if (composite_type->AsStruct()) { + composite_constant = MakeUnique( + composite_type->AsStruct(), constants); + } else if (composite_type->AsVector()) { + composite_constant = MakeUnique( + composite_type->AsVector(), constants); + } else { + assert(false && + "Precondition: |composite_type| must declare a composite type."); + return 0; + } + + uint32_t existing_constant = + GetIRContext()->get_constant_mgr()->FindDeclaredConstant( + composite_constant.get(), composite_type_instruction.result_id()); + if (existing_constant) { + return existing_constant; + } + uint32_t result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantComposite( + result, composite_type_instruction.result_id(), constant_ids)); + return result; +} + +uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite( + const opt::Instruction& composite_type_instruction, + uint32_t component_type_id, uint32_t num_components) { + std::vector zero_constants; + std::vector zero_ids; + uint32_t zero_component = FindOrCreateZeroConstant(component_type_id); + const opt::analysis::Constant* registered_zero_component = + GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component); + for (uint32_t i = 0; i < num_components; i++) { + zero_constants.push_back(registered_zero_component); + zero_ids.push_back(zero_component); + } + return FindOrCreateCompositeConstant(composite_type_instruction, + zero_constants, zero_ids); +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index e1e8aec25c..4b78f29a79 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -138,12 +138,85 @@ class FuzzerPass { // applied to add them. uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed); + // Returns the id of an OpConstant instruction, with 32-bit floating-point + // type, with |word| as its value. If either the required floating-point type + // or the constant do not exist, transformations are applied to add them. + uint32_t FindOrCreate32BitFloatConstant(uint32_t word); + + // Returns the id of an OpConstantTrue or OpConstantFalse instruction, + // according to |value|. If either the required instruction or the bool + // type do not exist, transformations are applied to add them. + uint32_t FindOrCreateBoolConstant(bool value); + // Returns the result id of an instruction of the form: // %id = OpUndef %|type_id| // If no such instruction exists, a transformation is applied to add it. uint32_t FindOrCreateGlobalUndef(uint32_t type_id); + // Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that: + // - base_type_ids captures every scalar or composite type declared in the + // module (i.e., all int, bool, float, vector, matrix, struct and array + // types + // - base_type_ids_to_pointers maps every such base type to the sequence + // of all pointer types that have storage class |storage_class| and the + // given base type as their pointee type. The sequence may be empty for + // some base types if no pointers to those types are defined for the given + // storage class, and the sequence will have multiple elements if there are + // repeated pointer declarations for the same base type and storage class. + std::pair, std::map>> + GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const; + + // Given a type id, |scalar_or_composite_type_id|, which must correspond to + // some scalar or composite type, returns the result id of an instruction + // defining a constant of the given type that is zero or false at everywhere. + // If such an instruction does not yet exist, transformations are applied to + // add it. + // + // Examples: + // --------------+------------------------------- + // TYPE | RESULT is id corresponding to + // --------------+------------------------------- + // bool | false + // --------------+------------------------------- + // bvec4 | (false, false, false, false) + // --------------+------------------------------- + // float | 0.0 + // --------------+------------------------------- + // vec2 | (0.0, 0.0) + // --------------+------------------------------- + // int[3] | [0, 0, 0] + // --------------+------------------------------- + // struct S { | + // int i; | S(0, false, (0u, 0u)) + // bool b; | + // uint2 u; | + // } | + // --------------+------------------------------- + uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id); + private: + // Array, matrix and vector are *homogeneous* composite types in the sense + // that every component of one of these types has the same type. Given a + // homogeneous composite type instruction, |composite_type_instruction|, + // returns the id of a composite constant instruction for which every element + // is zero/false. If such an instruction does not yet exist, transformations + // are applied to add it. + uint32_t GetZeroConstantForHomogeneousComposite( + const opt::Instruction& composite_type_instruction, + uint32_t component_type_id, uint32_t num_components); + + // Helper to find an existing composite constant instruction of the given + // composite type with the given constant components, or to apply + // transformations to create such an instruction if it does not yet exist. + // Parameter |composite_type_instruction| must be a composite type + // instruction. The parameters |constants| and |constant_ids| must have the + // same size, and it must be the case that for each i, |constant_ids[i]| is + // the result id of an instruction that defines |constants[i]|. + uint32_t FindOrCreateCompositeConstant( + const opt::Instruction& composite_type_instruction, + const std::vector& constants, + const std::vector& constant_ids); + opt::IRContext* ir_context_; FactManager* fact_manager_; FuzzerContext* fuzzer_context_; diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp new file mode 100644 index 0000000000..1371f46c62 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_global_variables.h" + +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_type_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default; + +void FuzzerPassAddGlobalVariables::Apply() { + auto base_type_ids_and_pointers = + GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate); + + // These are the base types that are available to this fuzzer pass. + auto& base_types = base_type_ids_and_pointers.first; + + // These are the pointers to those base types that are *initially* available + // to the fuzzer pass. The fuzzer pass might add pointer types in cases where + // none are available for a given base type. + auto& base_type_to_pointers = base_type_ids_and_pointers.second; + + // Probabilistically keep adding global variables. + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) { + // Choose a random base type; the new variable's type will be a pointer to + // this base type. + uint32_t base_type = + base_types[GetFuzzerContext()->RandomIndex(base_types)]; + uint32_t pointer_type_id; + std::vector& available_pointers_to_base_type = + base_type_to_pointers.at(base_type); + // Determine whether there is at least one pointer to this base type. + if (available_pointers_to_base_type.empty()) { + // There is not. Make one, to use here, and add it to the available + // pointers for the base type so that future variables can potentially + // use it. + pointer_type_id = GetFuzzerContext()->GetFreshId(); + available_pointers_to_base_type.push_back(pointer_type_id); + ApplyTransformation(TransformationAddTypePointer( + pointer_type_id, SpvStorageClassPrivate, base_type)); + } else { + // There is - grab one. + pointer_type_id = + available_pointers_to_base_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_base_type)]; + } + ApplyTransformation(TransformationAddGlobalVariable( + GetFuzzerContext()->GetFreshId(), pointer_type_id, + FindOrCreateZeroConstant(base_type), true)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h new file mode 100644 index 0000000000..c71d147748 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_global_variables.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds global variables, with Private storage class, +// to the module. +class FuzzerPassAddGlobalVariables : public FuzzerPass { + public: + FuzzerPassAddGlobalVariables( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddGlobalVariables(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp new file mode 100644 index 0000000000..8d6d80d632 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_local_variables.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_add_type_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default; + +void FuzzerPassAddLocalVariables::Apply() { + auto base_type_ids_and_pointers = + GetAvailableBaseTypesAndPointers(SpvStorageClassFunction); + + // These are the base types that are available to this fuzzer pass. + auto& base_types = base_type_ids_and_pointers.first; + + // These are the pointers to those base types that are *initially* available + // to the fuzzer pass. The fuzzer pass might add pointer types in cases where + // none are available for a given base type. + auto& base_type_to_pointers = base_type_ids_and_pointers.second; + + // Consider every function in the module. + for (auto& function : *GetIRContext()->module()) { + // Probabilistically keep adding random variables to this function. + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLocalVariable())) { + // Choose a random base type; the new variable's type will be a pointer to + // this base type. + uint32_t base_type = + base_types[GetFuzzerContext()->RandomIndex(base_types)]; + uint32_t pointer_type; + std::vector& available_pointers_to_base_type = + base_type_to_pointers.at(base_type); + // Determine whether there is at least one pointer to this base type. + if (available_pointers_to_base_type.empty()) { + // There is not. Make one, to use here, and add it to the available + // pointers for the base type so that future variables can potentially + // use it. + pointer_type = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypePointer( + pointer_type, SpvStorageClassFunction, base_type)); + available_pointers_to_base_type.push_back(pointer_type); + } else { + // There is - grab one. + pointer_type = + available_pointers_to_base_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_base_type)]; + } + ApplyTransformation(TransformationAddLocalVariable( + GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(), + FindOrCreateZeroConstant(base_type), true)); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h new file mode 100644 index 0000000000..ef002fb00b --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_local_variables.h @@ -0,0 +1,43 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +#include +#include + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds local variables, with Function storage class, +// to the module. +class FuzzerPassAddLocalVariables : public FuzzerPass { + public: + FuzzerPassAddLocalVariables( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddLocalVariables(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index f9f9969cdd..b81b17daba 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -391,6 +391,15 @@ uint32_t FindFunctionType(opt::IRContext* ir_context, return 0; } +opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) { + for (auto& function : *ir_context->module()) { + if (function.result_id() == function_id) { + return &function; + } + } + return nullptr; +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index f0a2953fd2..1cbc59fcdb 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -137,6 +137,10 @@ bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id); uint32_t FindFunctionType(opt::IRContext* ir_context, const std::vector& type_ids); +// Returns the function with result id |function_id|, or |nullptr| if no such +// function exists. +opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id); + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 52a3a788ca..67b362a817 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -339,6 +339,7 @@ message Transformation { TransformationAddGlobalUndef add_global_undef = 32; TransformationAddFunction add_function = 33; TransformationAddDeadBlock add_dead_block = 34; + TransformationAddLocalVariable add_local_variable = 35; // Add additional option using the next available number. } } @@ -507,15 +508,38 @@ message TransformationAddGlobalVariable { // Optional initializer; 0 if there is no initializer uint32 initializer_id = 3; - // True if and only if the value of the variable should be regarded, in - // general, as totally unknown and subject to change (even if, due to an - // initializer, the original value is known). This is the case for variables - // added when a module is donated, for example, and means that stores to such - // variables can be performed in an arbitrary fashion. + // True if and only if the behaviour of the module should not depend on the + // value of the variable, in which case stores to the variable can be + // performed in an arbitrary fashion. bool value_is_arbitrary = 4; } +message TransformationAddLocalVariable { + + // Adds a local variable of the given type (which must be a pointer with + // Function storage class) to the given function, initialized to the given + // id. + + // Fresh id for the local variable + uint32 fresh_id = 1; + + // The type of the local variable + uint32 type_id = 2; + + // The id of the function to which the local variable should be added + uint32 function_id = 3; + + // Initial value of the variable + uint32 initializer_id = 4; + + // True if and only if the behaviour of the module should not depend on the + // value of the variable, in which case stores to the variable can be + // performed in an arbitrary fashion. + bool value_is_arbitrary = 5; + +} + message TransformationAddNoContractionDecoration { // Applies OpDecorate NoContraction to the given result id diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index 8037af15e4..1ed4318390 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -26,6 +26,7 @@ #include "source/fuzz/transformation_add_function.h" #include "source/fuzz/transformation_add_global_undef.h" #include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_local_variable.h" #include "source/fuzz/transformation_add_no_contraction_decoration.h" #include "source/fuzz/transformation_add_type_array.h" #include "source/fuzz/transformation_add_type_boolean.h" @@ -85,6 +86,9 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kAddGlobalVariable: return MakeUnique( message.add_global_variable()); + case protobufs::Transformation::TransformationCase::kAddLocalVariable: + return MakeUnique( + message.add_local_variable()); case protobufs::Transformation::TransformationCase:: kAddNoContractionDecoration: return MakeUnique( diff --git a/source/fuzz/transformation_add_local_variable.cpp b/source/fuzz/transformation_add_local_variable.cpp new file mode 100644 index 0000000000..cdaea53bbf --- /dev/null +++ b/source/fuzz/transformation_add_local_variable.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_local_variable.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddLocalVariable::TransformationAddLocalVariable( + const spvtools::fuzz::protobufs::TransformationAddLocalVariable& message) + : message_(message) {} + +TransformationAddLocalVariable::TransformationAddLocalVariable( + uint32_t fresh_id, uint32_t type_id, uint32_t function_id, + uint32_t initializer_id, bool value_is_arbitrary) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_function_id(function_id); + message_.set_initializer_id(initializer_id); + message_.set_value_is_arbitrary(value_is_arbitrary); +} + +bool TransformationAddLocalVariable::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // The provided id must be fresh. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + // The pointer type id must indeed correspond to a pointer, and it must have + // function storage class. + auto type_instruction = + context->get_def_use_mgr()->GetDef(message_.type_id()); + if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer || + type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) { + return false; + } + // The initializer must... + auto initializer_instruction = + context->get_def_use_mgr()->GetDef(message_.initializer_id()); + // ... exist, ... + if (!initializer_instruction) { + return false; + } + // ... be a constant, ... + if (!spvOpcodeIsConstant(initializer_instruction->opcode())) { + return false; + } + // ... and have the same type as the pointee type. + if (initializer_instruction->type_id() != + type_instruction->GetSingleWordInOperand(1)) { + return false; + } + // The function to which the local variable is to be added must exist. + return fuzzerutil::FindFunction(context, message_.function_id()); +} + +void TransformationAddLocalVariable::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + fuzzerutil::FindFunction(context, message_.function_id()) + ->begin() + ->begin() + ->InsertBefore(MakeUnique( + context, SpvOpVariable, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_STORAGE_CLASS, + { + + SpvStorageClassFunction}}, + {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}}))); + if (message_.value_is_arbitrary()) { + fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id()); + } + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationAddLocalVariable::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_local_variable() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_add_local_variable.h b/source/fuzz/transformation_add_local_variable.h new file mode 100644 index 0000000000..6a97b7152e --- /dev/null +++ b/source/fuzz/transformation_add_local_variable.h @@ -0,0 +1,60 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_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 TransformationAddLocalVariable : public Transformation { + public: + explicit TransformationAddLocalVariable( + const protobufs::TransformationAddLocalVariable& message); + + TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id, + uint32_t function_id, uint32_t initializer_id, + bool value_is_arbitrary); + + // - |message_.fresh_id| must not be used by the module + // - |message_.type_id| must be the id of a pointer type with Function + // storage class + // - |message_.initializer_id| must be the id of a constant with the same + // type as the pointer's pointee type + // - |message_.function_id| must be the id of a function + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an instruction to the start of |message_.function_id|, of the form: + // |message_.fresh_id| = OpVariable |message_.type_id| Function + // |message_.initializer_id| + // If |message_.value_is_arbitrary| holds, adds a corresponding fact to + // |fact_manager|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddLocalVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 732d9fed05..d371326c11 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -33,6 +33,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_add_function_test.cpp transformation_add_global_undef_test.cpp transformation_add_global_variable_test.cpp + transformation_add_local_variable_test.cpp transformation_add_no_contraction_decoration_test.cpp transformation_add_type_array_test.cpp transformation_add_type_boolean_test.cpp diff --git a/test/fuzz/transformation_add_local_variable_test.cpp b/test/fuzz/transformation_add_local_variable_test.cpp new file mode 100644 index 0000000000..465af41f1e --- /dev/null +++ b/test/fuzz/transformation_add_local_variable_test.cpp @@ -0,0 +1,206 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_local_variable.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddLocalVariableTest, BasicTest) { + 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 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 3 + %16 = OpTypeArray %13 %15 + %17 = OpTypeBool + %18 = OpTypeStruct %16 %7 %17 + %19 = OpTypePointer Function %18 + %21 = OpConstant %13 1 + %22 = OpConstant %13 2 + %23 = OpConstant %13 4 + %24 = OpConstantComposite %16 %21 %22 %23 + %25 = OpConstant %6 5 + %26 = OpConstant %6 6 + %27 = OpConstantComposite %7 %25 %26 + %28 = OpConstantFalse %17 + %29 = OpConstantComposite %18 %24 %27 %28 + %30 = OpTypeVector %13 2 + %31 = OpTypePointer Function %30 + %33 = OpConstantComposite %30 %21 %21 + %34 = OpTypeVector %17 3 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %17 + %38 = OpConstantComposite %34 %37 %28 %28 + %39 = OpTypeVector %13 4 + %40 = OpTypeMatrix %39 3 + %41 = OpTypePointer Function %40 + %43 = OpConstantComposite %39 %21 %22 %23 %21 + %44 = OpConstantComposite %39 %22 %23 %21 %22 + %45 = OpConstantComposite %39 %23 %21 %22 %23 + %46 = OpConstantComposite %40 %43 %44 %45 + %50 = OpTypePointer Function %14 + %51 = OpConstantNull %14 + %4 = OpFunction %2 None %3 + %5 = 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; + + // A few cases of inapplicable transformations: + // Id 4 is already in use + ASSERT_FALSE(TransformationAddLocalVariable(4, 50, 4, 51, true) + .IsApplicable(context.get(), fact_manager)); + // Type mismatch between initializer and pointer + ASSERT_FALSE(TransformationAddLocalVariable(105, 46, 4, 51, true) + .IsApplicable(context.get(), fact_manager)); + // Id 5 is not a function + ASSERT_FALSE(TransformationAddLocalVariable(105, 50, 5, 51, true) + .IsApplicable(context.get(), fact_manager)); + + // %105 = OpVariable %50 Function %51 + { + TransformationAddLocalVariable transformation(105, 50, 4, 51, true); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + + // %104 = OpVariable %41 Function %46 + { + TransformationAddLocalVariable transformation(104, 41, 4, 46, false); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + + // %103 = OpVariable %35 Function %38 + { + TransformationAddLocalVariable transformation(103, 35, 4, 38, true); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + + // %102 = OpVariable %31 Function %33 + { + TransformationAddLocalVariable transformation(102, 31, 4, 33, false); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + + // %101 = OpVariable %19 Function %29 + { + TransformationAddLocalVariable transformation(101, 19, 4, 29, true); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + + // %100 = OpVariable %8 Function %12 + { + TransformationAddLocalVariable transformation(100, 8, 4, 12, false); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(100)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(101)); + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(102)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(103)); + ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(104)); + ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(105)); + + 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 3 + %16 = OpTypeArray %13 %15 + %17 = OpTypeBool + %18 = OpTypeStruct %16 %7 %17 + %19 = OpTypePointer Function %18 + %21 = OpConstant %13 1 + %22 = OpConstant %13 2 + %23 = OpConstant %13 4 + %24 = OpConstantComposite %16 %21 %22 %23 + %25 = OpConstant %6 5 + %26 = OpConstant %6 6 + %27 = OpConstantComposite %7 %25 %26 + %28 = OpConstantFalse %17 + %29 = OpConstantComposite %18 %24 %27 %28 + %30 = OpTypeVector %13 2 + %31 = OpTypePointer Function %30 + %33 = OpConstantComposite %30 %21 %21 + %34 = OpTypeVector %17 3 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %17 + %38 = OpConstantComposite %34 %37 %28 %28 + %39 = OpTypeVector %13 4 + %40 = OpTypeMatrix %39 3 + %41 = OpTypePointer Function %40 + %43 = OpConstantComposite %39 %21 %22 %23 %21 + %44 = OpConstantComposite %39 %22 %23 %21 %22 + %45 = OpConstantComposite %39 %23 %21 %22 %23 + %46 = OpConstantComposite %40 %43 %44 %45 + %50 = OpTypePointer Function %14 + %51 = OpConstantNull %14 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpVariable %8 Function %12 + %101 = OpVariable %19 Function %29 + %102 = OpVariable %31 Function %33 + %103 = OpVariable %35 Function %38 + %104 = OpVariable %41 Function %46 + %105 = OpVariable %50 Function %51 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools From fe10239f92f4539e9050da375dab095328fec196 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 6 Feb 2020 16:54:34 +0000 Subject: [PATCH 17/88] spirv-fuzz: Add fuzzer passes to add loads/stores (#3176) This change adds fuzzer passes that sprinkle loads and stores into a module at random, with stores restricted to occur in either dead blocks, or to use pointers for which it is known that the pointee value does not influence the module's overall behaviour. The change also generalises the VariableValueIsArbitrary fact to PointeeValueIsIrrelevant, to allow stores through access chains or object copies of variables whose values are known to be irrelevant. The change includes some other minor refactorings. --- source/fuzz/CMakeLists.txt | 8 + source/fuzz/fact_manager.cpp | 41 +-- source/fuzz/fact_manager.h | 23 +- source/fuzz/fuzzer.cpp | 8 + source/fuzz/fuzzer_context.cpp | 4 + source/fuzz/fuzzer_context.h | 4 + source/fuzz/fuzzer_pass_add_loads.cpp | 95 +++++ source/fuzz/fuzzer_pass_add_loads.h | 38 ++ source/fuzz/fuzzer_pass_add_local_variables.h | 3 - source/fuzz/fuzzer_pass_add_stores.cpp | 128 +++++++ source/fuzz/fuzzer_pass_add_stores.h | 40 ++ source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 6 +- source/fuzz/fuzzer_pass_copy_objects.cpp | 14 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 2 +- source/fuzz/fuzzer_util.cpp | 71 ++++ source/fuzz/fuzzer_util.h | 17 + source/fuzz/protobufs/spvtoolsfuzz.proto | 56 ++- source/fuzz/transformation.cpp | 6 + source/fuzz/transformation_add_function.cpp | 6 +- .../transformation_add_global_variable.cpp | 8 +- .../fuzz/transformation_add_global_variable.h | 5 +- .../transformation_add_local_variable.cpp | 8 +- .../fuzz/transformation_add_local_variable.h | 4 +- .../transformation_composite_construct.cpp | 23 +- source/fuzz/transformation_copy_object.cpp | 22 +- source/fuzz/transformation_copy_object.h | 5 +- source/fuzz/transformation_load.cpp | 106 ++++++ source/fuzz/transformation_load.h | 59 +++ .../fuzz/transformation_outline_function.cpp | 6 +- ...transformation_replace_id_with_synonym.cpp | 30 +- .../transformation_replace_id_with_synonym.h | 8 - source/fuzz/transformation_store.cpp | 128 +++++++ source/fuzz/transformation_store.h | 63 ++++ test/fuzz/CMakeLists.txt | 2 + .../fuzz/transformation_add_function_test.cpp | 70 ++-- ...ransformation_add_global_variable_test.cpp | 18 +- ...transformation_add_local_variable_test.cpp | 12 +- test/fuzz/transformation_copy_object_test.cpp | 52 +++ test/fuzz/transformation_load_test.cpp | 277 ++++++++++++++ .../transformation_outline_function_test.cpp | 48 +-- test/fuzz/transformation_store_test.cpp | 341 ++++++++++++++++++ 41 files changed, 1643 insertions(+), 222 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_add_loads.cpp create mode 100644 source/fuzz/fuzzer_pass_add_loads.h create mode 100644 source/fuzz/fuzzer_pass_add_stores.cpp create mode 100644 source/fuzz/fuzzer_pass_add_stores.h create mode 100644 source/fuzz/transformation_load.cpp create mode 100644 source/fuzz/transformation_load.h create mode 100644 source/fuzz/transformation_store.cpp create mode 100644 source/fuzz/transformation_store.h create mode 100644 test/fuzz/transformation_load_test.cpp create mode 100644 test/fuzz/transformation_store_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index b3c197034a..7166d5c441 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -41,8 +41,10 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_dead_continues.h fuzzer_pass_add_global_variables.h + fuzzer_pass_add_loads.h fuzzer_pass_add_local_variables.h fuzzer_pass_add_no_contraction_decorations.h + fuzzer_pass_add_stores.h fuzzer_pass_add_useful_constructs.h fuzzer_pass_adjust_function_controls.h fuzzer_pass_adjust_loop_controls.h @@ -90,6 +92,7 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_construct.h transformation_composite_extract.h transformation_copy_object.h + transformation_load.h transformation_merge_blocks.h transformation_move_block_down.h transformation_outline_function.h @@ -101,6 +104,7 @@ if(SPIRV_BUILD_FUZZER) transformation_set_memory_operands_mask.h transformation_set_selection_control.h transformation_split_block.h + transformation_store.h transformation_vector_shuffle.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -116,8 +120,10 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_dead_continues.cpp fuzzer_pass_add_global_variables.cpp + fuzzer_pass_add_loads.cpp fuzzer_pass_add_local_variables.cpp fuzzer_pass_add_no_contraction_decorations.cpp + fuzzer_pass_add_stores.cpp fuzzer_pass_add_useful_constructs.cpp fuzzer_pass_adjust_function_controls.cpp fuzzer_pass_adjust_loop_controls.cpp @@ -164,6 +170,7 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_construct.cpp transformation_composite_extract.cpp transformation_copy_object.cpp + transformation_load.cpp transformation_merge_blocks.cpp transformation_move_block_down.cpp transformation_outline_function.cpp @@ -175,6 +182,7 @@ if(SPIRV_BUILD_FUZZER) transformation_set_memory_operands_mask.cpp transformation_set_selection_control.cpp transformation_split_block.cpp + transformation_store.cpp transformation_vector_shuffle.cpp uniform_buffer_element_descriptor.cpp ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp index a7b431120a..486e8f546f 100644 --- a/source/fuzz/fact_manager.cpp +++ b/source/fuzz/fact_manager.cpp @@ -860,30 +860,30 @@ bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe( //============================== //============================== -// Arbitrarily-valued variable facts +// Irrelevant pointee value facts // The purpose of this class is to group the fields and data used to represent -// facts about livesafe functions. -class FactManager::ArbitrarilyValuedVaribleFacts { +// facts about pointers whose pointee values are irrelevant. +class FactManager::IrrelevantPointeeValueFacts { public: // See method in FactManager which delegates to this method. - void AddFact(const protobufs::FactValueOfVariableIsArbitrary& fact); + void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact); // See method in FactManager which delegates to this method. - bool VariableValueIsArbitrary(uint32_t variable_id) const; + bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; private: - std::set arbitrary_valued_varible_ids_; + std::set pointers_to_irrelevant_pointees_ids_; }; -void FactManager::ArbitrarilyValuedVaribleFacts::AddFact( - const protobufs::FactValueOfVariableIsArbitrary& fact) { - arbitrary_valued_varible_ids_.insert(fact.variable_id()); +void FactManager::IrrelevantPointeeValueFacts::AddFact( + const protobufs::FactPointeeValueIsIrrelevant& fact) { + pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id()); } -bool FactManager::ArbitrarilyValuedVaribleFacts::VariableValueIsArbitrary( - uint32_t variable_id) const { - return arbitrary_valued_varible_ids_.count(variable_id) != 0; +bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant( + uint32_t pointer_id) const { + return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0; } // End of arbitrarily-valued variable facts @@ -894,8 +894,8 @@ FactManager::FactManager() data_synonym_facts_(MakeUnique()), dead_block_facts_(MakeUnique()), livesafe_function_facts_(MakeUnique()), - arbitrarily_valued_variable_facts_( - MakeUnique()) {} + irrelevant_pointee_value_facts_( + MakeUnique()) {} FactManager::~FactManager() = default; @@ -1017,15 +1017,14 @@ void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) { livesafe_function_facts_->AddFact(fact); } -bool FactManager::VariableValueIsArbitrary(uint32_t variable_id) const { - return arbitrarily_valued_variable_facts_->VariableValueIsArbitrary( - variable_id); +bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const { + return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id); } -void FactManager::AddFactValueOfVariableIsArbitrary(uint32_t variable_id) { - protobufs::FactValueOfVariableIsArbitrary fact; - fact.set_variable_id(variable_id); - arbitrarily_valued_variable_facts_->AddFact(fact); +void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) { + protobufs::FactPointeeValueIsIrrelevant fact; + fact.set_pointer_id(pointer_id); + irrelevant_pointee_value_facts_->AddFact(fact); } } // namespace fuzz diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h index 117ed1c617..55cbfa04ef 100644 --- a/source/fuzz/fact_manager.h +++ b/source/fuzz/fact_manager.h @@ -64,9 +64,9 @@ class FactManager { // Records the fact that |function_id| is livesafe. void AddFactFunctionIsLivesafe(uint32_t function_id); - // Records the fact that |variable_id| has an arbitrary value and can thus be - // stored to without affecting the module's behaviour. - void AddFactValueOfVariableIsArbitrary(uint32_t variable_id); + // Records the fact that the value of the pointee associated with |pointer_id| + // is irrelevant: it does not affect the observable behaviour of the module. + void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id); // The fact manager is responsible for managing a few distinct categories of // facts. In principle there could be different fact managers for each kind @@ -161,12 +161,13 @@ class FactManager { //============================== //============================== - // Querying facts about arbitrarily-valued variables + // Querying facts about pointers with irrelevant pointee values - // Returns true if and ony if |variable_id| is arbitrarily-valued. - bool VariableValueIsArbitrary(uint32_t variable_id) const; + // Returns true if and ony if the value of the pointee associated with + // |pointer_id| is irrelevant. + bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; - // End of arbitrarily-valued variable facts + // End of irrelevant pointee value facts //============================== private: @@ -191,10 +192,10 @@ class FactManager { std::unique_ptr livesafe_function_facts_; // Unique pointer to internal data. - class ArbitrarilyValuedVaribleFacts; // Opaque class for management of - // facts about variables whose values should be expected to be arbitrary. - std::unique_ptr - arbitrarily_valued_variable_facts_; // Unique pointer to internal data. + class IrrelevantPointeeValueFacts; // Opaque class for management of + // facts about pointers whose pointee values do not matter. + std::unique_ptr + irrelevant_pointee_value_facts_; // Unique pointer to internal data. }; } // namespace fuzz diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 8caa853037..c66d8e5695 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -26,8 +26,10 @@ #include "source/fuzz/fuzzer_pass_add_dead_breaks.h" #include "source/fuzz/fuzzer_pass_add_dead_continues.h" #include "source/fuzz/fuzzer_pass_add_global_variables.h" +#include "source/fuzz/fuzzer_pass_add_loads.h" #include "source/fuzz/fuzzer_pass_add_local_variables.h" #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" +#include "source/fuzz/fuzzer_pass_add_stores.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" @@ -196,9 +198,15 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), &fact_manager, + &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), &fact_manager, + &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 0fb275851e..b84227e94f 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -30,10 +30,12 @@ const std::pair kChanceOfAddingDeadBlock = {20, 90}; const std::pair kChanceOfAddingDeadBreak = {5, 80}; const std::pair kChanceOfAddingDeadContinue = {5, 80}; const std::pair kChanceOfAddingGlobalVariable = {20, 90}; +const std::pair kChanceOfAddingLoad = {5, 50}; const std::pair kChanceOfAddingLocalVariable = {20, 90}; const std::pair kChanceOfAddingMatrixType = {20, 70}; const std::pair kChanceOfAddingNoContractionDecoration = { 5, 70}; +const std::pair kChanceOfAddingStore = {5, 50}; const std::pair kChanceOfAddingVectorType = {20, 70}; const std::pair kChanceOfAdjustingFunctionControl = {20, 70}; @@ -92,12 +94,14 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue); chance_of_adding_global_variable_ = ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable); + chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad); chance_of_adding_local_variable_ = ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable); chance_of_adding_matrix_type_ = ChooseBetweenMinAndMax(kChanceOfAddingMatrixType); chance_of_adding_no_contraction_decoration_ = ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration); + chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore); chance_of_adding_vector_type_ = ChooseBetweenMinAndMax(kChanceOfAddingVectorType); chance_of_adjusting_function_control_ = diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 2c48ac5a15..21f8a6279a 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -72,6 +72,7 @@ class FuzzerContext { uint32_t GetChanceOfAddingGlobalVariable() { return chance_of_adding_global_variable_; } + uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; } uint32_t GetChanceOfAddingLocalVariable() { return chance_of_adding_local_variable_; } @@ -81,6 +82,7 @@ class FuzzerContext { uint32_t GetChanceOfAddingNoContractionDecoration() { return chance_of_adding_no_contraction_decoration_; } + uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; } uint32_t GetChanceOfAddingVectorType() { return chance_of_adding_vector_type_; } @@ -155,9 +157,11 @@ class FuzzerContext { uint32_t chance_of_adding_dead_break_; uint32_t chance_of_adding_dead_continue_; uint32_t chance_of_adding_global_variable_; + uint32_t chance_of_adding_load_; uint32_t chance_of_adding_local_variable_; uint32_t chance_of_adding_matrix_type_; uint32_t chance_of_adding_no_contraction_decoration_; + uint32_t chance_of_adding_store_; uint32_t chance_of_adding_vector_type_; uint32_t chance_of_adjusting_function_control_; uint32_t chance_of_adjusting_loop_control_; diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp new file mode 100644 index 0000000000..7509bce4bf --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_loads.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_load.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLoads::FuzzerPassAddLoads( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddLoads::~FuzzerPassAddLoads() = default; + +void FuzzerPassAddLoads::Apply() { + MaybeAddTransformationBeforeEachInstruction( + [this](const opt::Function& function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(inst_it->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Check whether it is legitimate to insert a load before this + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { + return; + } + + // Randomly decide whether to try inserting a load here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLoad())) { + return; + } + + std::vector relevant_instructions = + FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + switch (instruction->result_id()) { + case SpvOpConstantNull: + case SpvOpUndef: + // Do not allow loading from a null or undefined pointer; + // this might be OK if the block is dead, but for now we + // conservatively avoid it. + return false; + default: + break; + } + return context->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == SpvOpTypePointer; + }); + + // At this point, |relevant_instructions| contains all the pointers + // we might think of loading from. + if (relevant_instructions.empty()) { + return; + } + + // Choose a pointer at random, and create and apply a loading + // transformation based on it. + ApplyTransformation(TransformationLoad( + GetFuzzerContext()->GetFreshId(), + relevant_instructions[GetFuzzerContext()->RandomIndex( + relevant_instructions)] + ->result_id(), + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h new file mode 100644 index 0000000000..125bc5dbfd --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_loads.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds stores, at random, from pointers in the module. +class FuzzerPassAddLoads : public FuzzerPass { + public: + FuzzerPassAddLoads(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddLoads(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h index ef002fb00b..eed36657ff 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.h +++ b/source/fuzz/fuzzer_pass_add_local_variables.h @@ -17,9 +17,6 @@ #include "source/fuzz/fuzzer_pass.h" -#include -#include - namespace spvtools { namespace fuzz { diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp new file mode 100644 index 0000000000..120c473226 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_stores.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_store.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddStores::FuzzerPassAddStores( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddStores::~FuzzerPassAddStores() = default; + +void FuzzerPassAddStores::Apply() { + MaybeAddTransformationBeforeEachInstruction( + [this](const opt::Function& function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(inst_it->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Check whether it is legitimate to insert a store before this + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting a store here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingStore())) { + return; + } + + // Look for pointers we might consider storing to. + std::vector relevant_pointers = + FindAvailableInstructions( + function, block, inst_it, + [this, block](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + auto type_inst = context->get_def_use_mgr()->GetDef( + instruction->type_id()); + if (type_inst->opcode() != SpvOpTypePointer) { + // Not a pointer. + return false; + } + if (type_inst->GetSingleWordInOperand(0) == + SpvStorageClassInput) { + // Read-only: cannot store to it. + return false; + } + switch (instruction->result_id()) { + case SpvOpConstantNull: + case SpvOpUndef: + // Do not allow storing to a null or undefined pointer; + // this might be OK if the block is dead, but for now we + // conservatively avoid it. + return false; + default: + break; + } + return GetFactManager()->BlockIsDead(block->id()) || + GetFactManager()->PointeeValueIsIrrelevant( + instruction->result_id()); + }); + + // At this point, |relevant_pointers| contains all the pointers we might + // think of storing to. + if (relevant_pointers.empty()) { + return; + } + + auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex( + relevant_pointers)]; + + std::vector relevant_values = + FindAvailableInstructions( + function, block, inst_it, + [pointer](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + return instruction->type_id() == + context->get_def_use_mgr() + ->GetDef(pointer->type_id()) + ->GetSingleWordInOperand(1); + }); + + if (relevant_values.empty()) { + return; + } + + // Choose a value at random, and create and apply a storing + // transformation based on it and the pointer. + ApplyTransformation(TransformationStore( + pointer->result_id(), + relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] + ->result_id(), + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h new file mode 100644 index 0000000000..9daa9e0f2e --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_stores.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds stores, at random, through pointers in the module, +// either (a) from dead blocks, or (b) through pointers whose pointee values +// are known not to affect the module's overall behaviour. +class FuzzerPassAddStores : public FuzzerPass { + public: + FuzzerPassAddStores(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddStores(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 6ff42ca79b..e932017df1 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -97,9 +97,9 @@ void FuzzerPassApplyIdSynonyms::Apply() { continue; } - if (!TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse( - GetIRContext(), use_inst, use_in_operand_index, - synonym_to_try->object())) { + if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst, + use_in_operand_index, + synonym_to_try->object())) { continue; } diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp index 35b15a38a3..48ed58854f 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -64,15 +64,11 @@ void FuzzerPassCopyObjects::Apply() { // 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(), instruction_descriptor, - GetFuzzerContext()->GetFreshId()); - assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && - "This transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetFactManager()); - *GetTransformations()->add_transformation() = - transformation.ToMessage(); + ApplyTransformation(TransformationCopyObject( + relevant_instructions[GetFuzzerContext()->RandomIndex( + relevant_instructions)] + ->result_id(), + instruction_descriptor, GetFuzzerContext()->GetFreshId())); }); } diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 75530b10e3..83cb18cffe 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -397,7 +397,7 @@ void FuzzerPassDonateModules::HandleTypesAndValues( // storage class global variable, using remapped versions of the result // type and initializer ids for the global variable in the donor. // - // We regard the added variable as having an arbitrary value. This + // We regard the added variable as having an irrelevant value. This // means that future passes can add stores to the variable in any // way they wish, and pass them as pointer parameters to functions // without worrying about whether their data might get modified. diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index b81b17daba..1af3fc8bad 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -400,6 +400,77 @@ opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) { return nullptr; } +bool IdIsAvailableAtUse(opt::IRContext* context, + opt::Instruction* use_instruction, + uint32_t use_input_operand_index, uint32_t id) { + auto defining_instruction = context->get_def_use_mgr()->GetDef(id); + auto enclosing_function = + context->get_instr_block(use_instruction)->GetParent(); + // If the id a function parameter, it needs to be associated with the + // function containing the use. + if (defining_instruction->opcode() == SpvOpFunctionParameter) { + return InstructionIsFunctionParameter(defining_instruction, + enclosing_function); + } + if (!context->get_instr_block(id)) { + // The id must be at global scope. + return true; + } + if (defining_instruction == use_instruction) { + // It is not OK for a definition to use itself. + return false; + } + auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); + if (use_instruction->opcode() == SpvOpPhi) { + // In the case where the use is an operand to OpPhi, it is actually the + // *parent* block associated with the operand that must be dominated by + // the synonym. + auto parent_block = + use_instruction->GetSingleWordInOperand(use_input_operand_index + 1); + return dominator_analysis->Dominates( + context->get_instr_block(defining_instruction)->id(), parent_block); + } + return dominator_analysis->Dominates(defining_instruction, use_instruction); +} + +bool IdIsAvailableBeforeInstruction(opt::IRContext* context, + opt::Instruction* instruction, + uint32_t id) { + auto defining_instruction = context->get_def_use_mgr()->GetDef(id); + auto enclosing_function = context->get_instr_block(instruction)->GetParent(); + // If the id a function parameter, it needs to be associated with the + // function containing the instruction. + if (defining_instruction->opcode() == SpvOpFunctionParameter) { + return InstructionIsFunctionParameter(defining_instruction, + enclosing_function); + } + if (!context->get_instr_block(id)) { + // The id is at global scope. + return true; + } + if (defining_instruction == instruction) { + // The instruction is not available right before its own definition. + return false; + } + return context->GetDominatorAnalysis(enclosing_function) + ->Dominates(defining_instruction, instruction); +} + +bool InstructionIsFunctionParameter(opt::Instruction* instruction, + opt::Function* function) { + if (instruction->opcode() != SpvOpFunctionParameter) { + return false; + } + bool found_parameter = false; + function->ForEachParam( + [instruction, &found_parameter](opt::Instruction* param) { + if (param == instruction) { + found_parameter = true; + } + }); + return found_parameter; +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 1cbc59fcdb..3e9c6911a1 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -141,6 +141,23 @@ uint32_t FindFunctionType(opt::IRContext* ir_context, // function exists. opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id); +// Checks whether |id| is available (according to dominance rules) at the use +// point defined by input operand |use_input_operand_index| of +// |use_instruction|. +bool IdIsAvailableAtUse(opt::IRContext* context, + opt::Instruction* use_instruction, + uint32_t use_input_operand_index, uint32_t id); + +// Checks whether |id| is available (according to dominance rules) at the +// program point directly before |instruction|. +bool IdIsAvailableBeforeInstruction(opt::IRContext* context, + opt::Instruction* instruction, uint32_t id); + +// Returns true if and only if |instruction| is an OpFunctionParameter +// associated with |function|. +bool InstructionIsFunctionParameter(opt::Instruction* instruction, + opt::Function* function); + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 67b362a817..08af3e046b 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -168,7 +168,7 @@ message Fact { FactDataSynonym data_synonym_fact = 2; FactBlockIsDead block_is_dead_fact = 3; FactFunctionIsLivesafe function_is_livesafe_fact = 4; - FactValueOfVariableIsArbitrary value_of_variable_is_arbitrary = 5; + FactPointeeValueIsIrrelevant pointee_value_is_irrelevant = 5; } } @@ -223,17 +223,15 @@ message FactFunctionIsLivesafe { uint32 function_id = 1; } -message FactValueOfVariableIsArbitrary { +message FactPointeeValueIsIrrelevant { - // Records the fact that the value stored in the variable or function - // parameter with the given id can be arbitrary: the module's observable - // behaviour does not depend on it. This means that arbitrary stores can be - // made to the variable, and that nothing can be guaranteed about values - // loaded from the variable. + // Records the fact that value of the data pointed to by a pointer id does + // not influence the observable behaviour of the module. This means that + // arbitrary stores can be made through the pointer, and that nothing can be + // guaranteed about the values that are loaded via the pointer. - // The result id of an OpVariable instruction, or an OpFunctionParameter - // instruction with pointer type - uint32 variable_id = 1; + // A result id of pointer type + uint32 pointer_id = 1; } message AccessChainClampingInfo { @@ -340,6 +338,8 @@ message Transformation { TransformationAddFunction add_function = 33; TransformationAddDeadBlock add_dead_block = 34; TransformationAddLocalVariable add_local_variable = 35; + TransformationLoad load = 36; + TransformationStore store = 37; // Add additional option using the next available number. } } @@ -511,7 +511,7 @@ message TransformationAddGlobalVariable { // True if and only if the behaviour of the module should not depend on the // value of the variable, in which case stores to the variable can be // performed in an arbitrary fashion. - bool value_is_arbitrary = 4; + bool value_is_irrelevant = 4; } @@ -536,7 +536,7 @@ message TransformationAddLocalVariable { // True if and only if the behaviour of the module should not depend on the // value of the variable, in which case stores to the variable can be // performed in an arbitrary fashion. - bool value_is_arbitrary = 5; + bool value_is_irrelevant = 5; } @@ -731,6 +731,22 @@ message TransformationCopyObject { } +message TransformationLoad { + + // Transformation that adds an OpLoad instruction from a pointer into an id. + + // The result of the load instruction + uint32 fresh_id = 1; + + // The pointer to be loaded from + uint32 pointer_id = 2; + + // A descriptor for an instruction in a block before which the new OpLoad + // instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 3; + +} + message TransformationMergeBlocks { // A transformation that merges a block with its predecessor. @@ -943,6 +959,22 @@ message TransformationSplitBlock { } +message TransformationStore { + + // Transformation that adds an OpStore instruction of an id to a pointer. + + // The pointer to be stored to + uint32 pointer_id = 1; + + // The value to be stored + uint32 value_id = 2; + + // A descriptor for an instruction in a block before which the new OpStore + // instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 3; + +} + message TransformationVectorShuffle { // A transformation that adds a vector shuffle instruction. diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index 1ed4318390..7d32dc0640 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -40,6 +40,7 @@ #include "source/fuzz/transformation_composite_construct.h" #include "source/fuzz/transformation_composite_extract.h" #include "source/fuzz/transformation_copy_object.h" +#include "source/fuzz/transformation_load.h" #include "source/fuzz/transformation_merge_blocks.h" #include "source/fuzz/transformation_move_block_down.h" #include "source/fuzz/transformation_outline_function.h" @@ -51,6 +52,7 @@ #include "source/fuzz/transformation_set_memory_operands_mask.h" #include "source/fuzz/transformation_set_selection_control.h" #include "source/fuzz/transformation_split_block.h" +#include "source/fuzz/transformation_store.h" #include "source/fuzz/transformation_vector_shuffle.h" #include "source/util/make_unique.h" @@ -122,6 +124,8 @@ std::unique_ptr Transformation::FromMessage( message.composite_extract()); case protobufs::Transformation::TransformationCase::kCopyObject: return MakeUnique(message.copy_object()); + case protobufs::Transformation::TransformationCase::kLoad: + return MakeUnique(message.load()); case protobufs::Transformation::TransformationCase::kMergeBlocks: return MakeUnique(message.merge_blocks()); case protobufs::Transformation::TransformationCase::kMoveBlockDown: @@ -154,6 +158,8 @@ std::unique_ptr Transformation::FromMessage( message.set_selection_control()); case protobufs::Transformation::TransformationCase::kSplitBlock: return MakeUnique(message.split_block()); + case protobufs::Transformation::TransformationCase::kStore: + return MakeUnique(message.store()); case protobufs::Transformation::TransformationCase::kVectorShuffle: return MakeUnique(message.vector_shuffle()); case protobufs::Transformation::TRANSFORMATION_NOT_SET: diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp index b013d9441c..120b3df9f3 100644 --- a/source/fuzz/transformation_add_function.cpp +++ b/source/fuzz/transformation_add_function.cpp @@ -155,7 +155,7 @@ void TransformationAddFunction::Apply( // that |success| is not used). // Record the fact that all pointer parameters and variables declared in the - // function should be regarded as having arbitrary values. This allows other + // function should be regarded as having irrelevant values. This allows other // passes to store arbitrarily to such variables, and to pass them freely as // parameters to other functions knowing that it is OK if they get // over-written. @@ -165,12 +165,12 @@ void TransformationAddFunction::Apply( if (context->get_def_use_mgr() ->GetDef(instruction.result_type_id()) ->opcode() == SpvOpTypePointer) { - fact_manager->AddFactValueOfVariableIsArbitrary( + fact_manager->AddFactValueOfPointeeIsIrrelevant( instruction.result_id()); } break; case SpvOpVariable: - fact_manager->AddFactValueOfVariableIsArbitrary( + fact_manager->AddFactValueOfPointeeIsIrrelevant( instruction.result_id()); break; default: diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp index c08517f9a5..7af5888608 100644 --- a/source/fuzz/transformation_add_global_variable.cpp +++ b/source/fuzz/transformation_add_global_variable.cpp @@ -25,11 +25,11 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable( TransformationAddGlobalVariable::TransformationAddGlobalVariable( uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id, - bool value_is_arbitrary) { + bool value_is_irrelevant) { message_.set_fresh_id(fresh_id); message_.set_type_id(type_id); message_.set_initializer_id(initializer_id); - message_.set_value_is_arbitrary(value_is_arbitrary); + message_.set_value_is_irrelevant(value_is_irrelevant); } bool TransformationAddGlobalVariable::IsApplicable( @@ -101,8 +101,8 @@ void TransformationAddGlobalVariable::Apply( } } - if (message_.value_is_arbitrary()) { - fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id()); + if (message_.value_is_irrelevant()) { + fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); } // We have added an instruction to the module, so need to be careful about the diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h index 406c915715..920ac45dc4 100644 --- a/source/fuzz/transformation_add_global_variable.h +++ b/source/fuzz/transformation_add_global_variable.h @@ -30,7 +30,7 @@ class TransformationAddGlobalVariable : public Transformation { TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id, - bool value_is_arbitrary); + bool value_is_irrelevant); // - |message_.fresh_id| must be fresh // - |message_.type_id| must be the id of a pointer type with Private storage @@ -44,6 +44,9 @@ class TransformationAddGlobalVariable : public Transformation { // |message_.type_id| and either no initializer or |message_.initializer_id| // as an initializer, depending on whether |message_.initializer_id| is 0. // The global variable has result id |message_.fresh_id|. + // + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to + // |fact_manager|. void Apply(opt::IRContext* context, FactManager* fact_manager) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_local_variable.cpp b/source/fuzz/transformation_add_local_variable.cpp index cdaea53bbf..69e536df16 100644 --- a/source/fuzz/transformation_add_local_variable.cpp +++ b/source/fuzz/transformation_add_local_variable.cpp @@ -25,12 +25,12 @@ TransformationAddLocalVariable::TransformationAddLocalVariable( TransformationAddLocalVariable::TransformationAddLocalVariable( uint32_t fresh_id, uint32_t type_id, uint32_t function_id, - uint32_t initializer_id, bool value_is_arbitrary) { + uint32_t initializer_id, bool value_is_irrelevant) { message_.set_fresh_id(fresh_id); message_.set_type_id(type_id); message_.set_function_id(function_id); message_.set_initializer_id(initializer_id); - message_.set_value_is_arbitrary(value_is_arbitrary); + message_.set_value_is_irrelevant(value_is_irrelevant); } bool TransformationAddLocalVariable::IsApplicable( @@ -82,8 +82,8 @@ void TransformationAddLocalVariable::Apply( SpvStorageClassFunction}}, {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}}))); - if (message_.value_is_arbitrary()) { - fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id()); + if (message_.value_is_irrelevant()) { + fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); } context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } diff --git a/source/fuzz/transformation_add_local_variable.h b/source/fuzz/transformation_add_local_variable.h index 6a97b7152e..b8e00ddfb4 100644 --- a/source/fuzz/transformation_add_local_variable.h +++ b/source/fuzz/transformation_add_local_variable.h @@ -30,7 +30,7 @@ class TransformationAddLocalVariable : public Transformation { TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id, uint32_t function_id, uint32_t initializer_id, - bool value_is_arbitrary); + bool value_is_irrelevant); // - |message_.fresh_id| must not be used by the module // - |message_.type_id| must be the id of a pointer type with Function @@ -44,7 +44,7 @@ class TransformationAddLocalVariable : public Transformation { // Adds an instruction to the start of |message_.function_id|, of the form: // |message_.fresh_id| = OpVariable |message_.type_id| Function // |message_.initializer_id| - // If |message_.value_is_arbitrary| holds, adds a corresponding fact to + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to // |fact_manager|. void Apply(opt::IRContext* context, FactManager* fact_manager) const override; diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp index 7a3aff1458..9c63c1d98a 100644 --- a/source/fuzz/transformation_composite_construct.cpp +++ b/source/fuzz/transformation_composite_construct.cpp @@ -84,27 +84,8 @@ bool TransformationCompositeConstruct::IsApplicable( // 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. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + component)) { return false; } } diff --git a/source/fuzz/transformation_copy_object.cpp b/source/fuzz/transformation_copy_object.cpp index af1e81c37e..bfdced37fe 100644 --- a/source/fuzz/transformation_copy_object.cpp +++ b/source/fuzz/transformation_copy_object.cpp @@ -64,20 +64,10 @@ bool TransformationCopyObject::IsApplicable( return false; } - // |message_object| must be available at the point where we want to add the - // copy. It is available if it is at global scope (in which case it has no - // block), or if it dominates the point of insertion but is different from the - // point of insertion. - // - // The reason why the object needs to be different from the insertion point is - // that the copy will be added *before* this point, and we do not want to - // insert it before the object's defining instruction. - return !context->get_instr_block(object_inst) || - (object_inst != &*insert_before && - context - ->GetDominatorAnalysis( - context->get_instr_block(insert_before)->GetParent()) - ->Dominates(object_inst, &*insert_before)); + // |message_object| must be available directly before the point where we want + // to add the copy. + return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + message_.object()); } void TransformationCopyObject::Apply(opt::IRContext* context, @@ -105,6 +95,10 @@ void TransformationCopyObject::Apply(opt::IRContext* context, fact_manager->AddFactDataSynonym(MakeDataDescriptor(message_.object(), {}), MakeDataDescriptor(message_.fresh_id(), {}), context); + + if (fact_manager->PointeeValueIsIrrelevant(message_.object())) { + fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); + } } protobufs::Transformation TransformationCopyObject::ToMessage() const { diff --git a/source/fuzz/transformation_copy_object.h b/source/fuzz/transformation_copy_object.h index ac5e978df6..9e9c26a341 100644 --- a/source/fuzz/transformation_copy_object.h +++ b/source/fuzz/transformation_copy_object.h @@ -57,7 +57,10 @@ class TransformationCopyObject : public Transformation { // 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. + // is added to |fact_manager|. + // - If |message_.object| is a pointer whose pointee value is known to be + // irrelevant, the analogous fact is added to |fact_manager| about + // |message_.fresh_id|. void Apply(opt::IRContext* context, FactManager* fact_manager) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp new file mode 100644 index 0000000000..ab6b8acce8 --- /dev/null +++ b/source/fuzz/transformation_load.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_load.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationLoad::TransformationLoad( + const spvtools::fuzz::protobufs::TransformationLoad& message) + : message_(message) {} + +TransformationLoad::TransformationLoad( + uint32_t fresh_id, uint32_t pointer_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_pointer_id(pointer_id); + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationLoad::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + + // The pointer must exist and have a type. + auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + // The type must indeed be a pointer type. + auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id()); + assert(pointer_type && "Type id must be defined."); + if (pointer_type->opcode() != SpvOpTypePointer) { + return false; + } + // We do not want to allow loading from null or undefined pointers, as it is + // not clear how punishing the consequences of doing so are from a semantics + // point of view. + switch (pointer->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + return false; + default: + break; + } + + // Determine which instruction we should be inserting before. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), context); + // It must exist, ... + if (!insert_before) { + return false; + } + // ... and it must be legitimate to insert a store before it. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { + return false; + } + + // The pointer needs to be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + message_.pointer_id()); +} + +void TransformationLoad::Apply(opt::IRContext* context, + spvtools::fuzz::FactManager* /*unused*/) const { + uint32_t result_type = context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(message_.pointer_id()) + ->type_id()) + ->GetSingleWordInOperand(1); + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + FindInstruction(message_.instruction_to_insert_before(), context) + ->InsertBefore(MakeUnique( + context, SpvOpLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}))); + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationLoad::ToMessage() const { + protobufs::Transformation result; + *result.mutable_load() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h new file mode 100644 index 0000000000..ff99016734 --- /dev/null +++ b/source/fuzz/transformation_load.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ +#define SOURCE_FUZZ_TRANSFORMATION_LOAD_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 TransformationLoad : public Transformation { + public: + explicit TransformationLoad(const protobufs::TransformationLoad& message); + + TransformationLoad( + uint32_t fresh_id, uint32_t pointer_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh + // - |message_.pointer_id| must be the id of a pointer + // - The pointer must not be OpConstantNull or OpUndef + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to insert an OpLoad, and where + // |message_.pointer_id| is available (according to dominance rules) + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an instruction of the form: + // |message_.fresh_id| = OpLoad %type |message_.pointer_id| + // before the instruction identified by + // |message_.instruction_to_insert_before|, where %type is the pointer's + // pointee type. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationLoad message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 7bbac54839..01d1c45834 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -617,10 +617,10 @@ TransformationOutlineFunction::PrepareFunctionPrototype( context, SpvOpFunctionParameter, context->get_def_use_mgr()->GetDef(id)->type_id(), input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList())); - // If the input id is an arbitrary-valued variable, the same should be true + // If the input id is an irrelevant-valued variable, the same should be true // of the corresponding parameter. - if (fact_manager->VariableValueIsArbitrary(id)) { - fact_manager->AddFactValueOfVariableIsArbitrary( + if (fact_manager->PointeeValueIsIrrelevant(id)) { + fact_manager->AddFactValueOfPointeeIsIrrelevant( input_id_to_fresh_id_map.at(id)); } } diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp index 79ba012829..88c977a240 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -65,9 +65,9 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( // The transformation is applicable if the synonymous id is available at the // use point. - return IdsIsAvailableAtUse(context, use_instruction, - message_.id_use_descriptor().in_operand_index(), - message_.synonymous_id()); + return fuzzerutil::IdIsAvailableAtUse( + context, use_instruction, message_.id_use_descriptor().in_operand_index(), + message_.synonymous_id()); } void TransformationReplaceIdWithSynonym::Apply( @@ -88,30 +88,6 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() return result; } -bool TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse( - opt::IRContext* context, opt::Instruction* use_instruction, - uint32_t use_input_operand_index, uint32_t id) { - if (!context->get_instr_block(id)) { - return true; - } - auto defining_instruction = context->get_def_use_mgr()->GetDef(id); - if (defining_instruction == use_instruction) { - return false; - } - auto dominator_analysis = context->GetDominatorAnalysis( - context->get_instr_block(use_instruction)->GetParent()); - if (use_instruction->opcode() == SpvOpPhi) { - // In the case where the use is an operand to OpPhi, it is actually the - // *parent* block associated with the operand that must be dominated by - // the synonym. - auto parent_block = - use_instruction->GetSingleWordInOperand(use_input_operand_index + 1); - return dominator_analysis->Dominates( - context->get_instr_block(defining_instruction)->id(), parent_block); - } - return dominator_analysis->Dominates(defining_instruction, use_instruction); -} - bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym( opt::IRContext* context, opt::Instruction* use_instruction, uint32_t use_in_operand_index) { diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h index c21673dcdd..48132c1e0f 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.h +++ b/source/fuzz/transformation_replace_id_with_synonym.h @@ -51,14 +51,6 @@ class TransformationReplaceIdWithSynonym : public Transformation { protobufs::Transformation ToMessage() const override; - // Checks whether the |id| is available (according to dominance rules) at the - // use point defined by input operand |use_input_operand_index| of - // |use_instruction|. - static bool IdsIsAvailableAtUse(opt::IRContext* context, - opt::Instruction* use_instruction, - uint32_t use_input_operand_index, - uint32_t id); - // Checks whether various conditions hold related to the acceptability of // replacing the id use at |use_in_operand_index| of |use_instruction| with // a synonym. In particular, this checks that: diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp new file mode 100644 index 0000000000..7cb761126f --- /dev/null +++ b/source/fuzz/transformation_store.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_store.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationStore::TransformationStore( + const spvtools::fuzz::protobufs::TransformationStore& message) + : message_(message) {} + +TransformationStore::TransformationStore( + uint32_t pointer_id, uint32_t value_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_pointer_id(pointer_id); + message_.set_value_id(value_id); + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationStore::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& fact_manager) const { + // The pointer must exist and have a type. + auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + + // The pointer type must indeed be a pointer. + auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id()); + assert(pointer_type && "Type id must be defined."); + if (pointer_type->opcode() != SpvOpTypePointer) { + return false; + } + + // The pointer must not be read only. + if (pointer_type->GetSingleWordInOperand(0) == SpvStorageClassInput) { + return false; + } + + // We do not want to allow storing to null or undefined pointers. + switch (pointer->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + return false; + default: + break; + } + + // Determine which instruction we should be inserting before. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), context); + // It must exist, ... + if (!insert_before) { + return false; + } + // ... and it must be legitimate to insert a store before it. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, + insert_before)) { + return false; + } + + // The block we are inserting into needs to be dead, or else the pointee type + // of the pointer we are storing to needs to be irrelevant (otherwise the + // store could impact on the observable behaviour of the module). + if (!fact_manager.BlockIsDead( + context->get_instr_block(insert_before)->id()) && + !fact_manager.PointeeValueIsIrrelevant(message_.pointer_id())) { + return false; + } + + // The value being stored needs to exist and have a type. + auto value = context->get_def_use_mgr()->GetDef(message_.value_id()); + if (!value || !value->type_id()) { + return false; + } + + // The type of the value must match the pointee type. + if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) { + return false; + } + + // The pointer needs to be available at the insertion point. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + message_.pointer_id())) { + return false; + } + + // The value needs to be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + message_.value_id()); +} + +void TransformationStore::Apply(opt::IRContext* context, + spvtools::fuzz::FactManager* /*unused*/) const { + FindInstruction(message_.instruction_to_insert_before(), context) + ->InsertBefore(MakeUnique( + context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationStore::ToMessage() const { + protobufs::Transformation result; + *result.mutable_store() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h new file mode 100644 index 0000000000..699afdda03 --- /dev/null +++ b/source/fuzz/transformation_store.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_STORE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_STORE_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 TransformationStore : public Transformation { + public: + explicit TransformationStore(const protobufs::TransformationStore& message); + + TransformationStore( + uint32_t pointer_id, uint32_t value_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.pointer_id| must be the id of a pointer + // - The pointer type must not have read-only storage class + // - The pointer must not be OpConstantNull or OpUndef + // - |message_.value_id| must be an instruction result id that has the same + // type as the pointee type of |message_.pointer_id| + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to insert an OpStore, and where both + // |message_.pointer_id| and |message_.value_id| are available (according + // to dominance rules) + // - Either the insertion point must be in a dead block, or it must be known + // that the pointee value of |message_.pointer_id| is irrelevant + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an instruction of the form: + // OpStore |pointer_id| |value_id| + // before the instruction identified by + // |message_.instruction_to_insert_before|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationStore message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_STORE_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index d371326c11..29d33ec60c 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -47,6 +47,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_composite_construct_test.cpp transformation_composite_extract_test.cpp transformation_copy_object_test.cpp + transformation_load_test.cpp transformation_merge_blocks_test.cpp transformation_move_block_down_test.cpp transformation_outline_function_test.cpp @@ -58,6 +59,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_set_memory_operands_mask_test.cpp transformation_set_selection_control_test.cpp transformation_split_block_test.cpp + transformation_store_test.cpp transformation_vector_shuffle_test.cpp uniform_buffer_element_descriptor_test.cpp) diff --git a/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp index 040d27ca5e..aed12dc06a 100644 --- a/test/fuzz/transformation_add_function_test.cpp +++ b/test/fuzz/transformation_add_function_test.cpp @@ -59,11 +59,11 @@ std::vector GetInstructionsForFunction( } // Returns true if and only if every pointer parameter and variable associated -// with |function_id| in |context| is known by |fact_manager| to be arbitrary, -// with the exception of |loop_limiter_id|, which must not be arbitrary. (It +// with |function_id| in |context| is known by |fact_manager| to be irrelevant, +// with the exception of |loop_limiter_id|, which must not be irrelevant. (It // can be 0 if no loop limiter is expected, and 0 should not be deemed -// arbitrary). -bool AllVariablesAndParametersExceptLoopLimiterAreArbitrary( +// irrelevant). +bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( opt::IRContext* context, const FactManager& fact_manager, uint32_t function_id, uint32_t loop_limiter_id) { // Look at all the functions until the function of interest is found. @@ -71,19 +71,19 @@ bool AllVariablesAndParametersExceptLoopLimiterAreArbitrary( if (function.result_id() != function_id) { continue; } - // Check that the parameters are all arbitrary. - bool found_non_arbitrary_parameter = false; + // Check that the parameters are all irrelevant. + bool found_non_irrelevant_parameter = false; function.ForEachParam( [context, &fact_manager, - &found_non_arbitrary_parameter](opt::Instruction* inst) { + &found_non_irrelevant_parameter](opt::Instruction* inst) { if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == SpvOpTypePointer && - !fact_manager.VariableValueIsArbitrary(inst->result_id())) { - found_non_arbitrary_parameter = true; + !fact_manager.PointeeValueIsIrrelevant(inst->result_id())) { + found_non_irrelevant_parameter = true; } }); - if (found_non_arbitrary_parameter) { - // A non-arbitrary parameter was found. + if (found_non_irrelevant_parameter) { + // A non-irrelevant parameter was found. return false; } // Look through the instructions in the function's first block. @@ -93,10 +93,10 @@ bool AllVariablesAndParametersExceptLoopLimiterAreArbitrary( // past all variables, so we are done. return true; } - // The variable should be arbitrary if and only if it is not the loop + // The variable should be irrelevant if and only if it is not the loop // limiter. if ((inst.result_id() == loop_limiter_id) == - fact_manager.VariableValueIsArbitrary(inst.result_id())) { + fact_manager.PointeeValueIsIrrelevant(inst.result_id())) { return false; } } @@ -633,8 +633,8 @@ TEST(TransformationAddFunctionTest, LoopLimiters) { ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(30)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context1.get(), fact_manager1, 30, 0)); std::string added_as_dead_code = R"( @@ -717,9 +717,9 @@ TEST(TransformationAddFunctionTest, LoopLimiters) { ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(30)); - // All variables/parameters in the function should be deemed arbitrary, + // All variables/parameters in the function should be deemed irrelevant, // except the loop limiter. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context2.get(), fact_manager2, 30, 100)); std::string added_as_livesafe_code = R"( OpCapability Shader @@ -848,8 +848,8 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context1.get(), fact_manager1, 10, 0)); std::string added_as_dead_code = R"( @@ -893,8 +893,8 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context2.get(), fact_manager2, 10, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader @@ -996,8 +996,8 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context1.get(), fact_manager1, 10, 0)); std::string added_as_dead_code = R"( @@ -1042,8 +1042,8 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context2.get(), fact_manager2, 10, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader @@ -1276,8 +1276,8 @@ TEST(TransformationAddFunctionTest, ClampedAccessChains) { ASSERT_TRUE(IsValid(env, context1.get())); // The function should not be deemed livesafe ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(12)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context1.get(), fact_manager1, 12, 0)); std::string added_as_dead_code = R"( @@ -1415,8 +1415,8 @@ TEST(TransformationAddFunctionTest, ClampedAccessChains) { ASSERT_TRUE(IsValid(env, context2.get())); // The function should be deemed livesafe ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(12)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context2.get(), fact_manager2, 12, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader @@ -1599,8 +1599,8 @@ TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { ASSERT_TRUE(IsValid(env, context1.get())); // The function should not be deemed livesafe ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context1.get(), fact_manager1, 8, 0)); std::string added_as_live_or_dead_code = R"( @@ -1636,8 +1636,8 @@ TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { ASSERT_TRUE(IsValid(env, context2.get())); // The function should be deemed livesafe ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(8)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context2.get(), fact_manager2, 8, 0)); ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get())); } @@ -1690,8 +1690,8 @@ TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { ASSERT_TRUE(IsValid(env, context1.get())); // The function should not be deemed livesafe ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); - // All variables/parameters in the function should be deemed arbitrary. - ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreArbitrary( + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( context1.get(), fact_manager1, 8, 0)); std::string added_as_dead_code = R"( diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp index 7fb4fa0860..d43a2ae362 100644 --- a/test/fuzz/transformation_add_global_variable_test.cpp +++ b/test/fuzz/transformation_add_global_variable_test.cpp @@ -125,12 +125,12 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); transformation.Apply(context.get(), &fact_manager); } - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(100)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(102)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(104)); - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(101)); - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(103)); - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(105)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(104)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105)); ASSERT_TRUE(IsValid(env, context.get())); @@ -234,9 +234,9 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); transformation.Apply(context.get(), &fact_manager); } - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(100)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(102)); - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(101)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( diff --git a/test/fuzz/transformation_add_local_variable_test.cpp b/test/fuzz/transformation_add_local_variable_test.cpp index 465af41f1e..fd7047f871 100644 --- a/test/fuzz/transformation_add_local_variable_test.cpp +++ b/test/fuzz/transformation_add_local_variable_test.cpp @@ -133,12 +133,12 @@ TEST(TransformationAddLocalVariableTest, BasicTest) { transformation.Apply(context.get(), &fact_manager); } - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(100)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(101)); - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(102)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(103)); - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(104)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(105)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(101)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(103)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(105)); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp index a33f58de3e..b85f75b458 100644 --- a/test/fuzz/transformation_copy_object_test.cpp +++ b/test/fuzz/transformation_copy_object_test.cpp @@ -626,6 +626,58 @@ TEST(TransformationCopyObjectTest, DoNotCopyNullOrUndefPointers) { .IsApplicable(context.get(), fact_manager)); } +TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) { + // Checks that if a pointer is known to have an irrelevant value, the same + // holds after the pointer is copied. + + 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %9 = OpVariable %7 Function + 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; + fact_manager.AddFactValueOfPointeeIsIrrelevant(8); + + TransformationCopyObject transformation1( + 8, MakeInstructionDescriptor(9, SpvOpReturn, 0), 100); + TransformationCopyObject transformation2( + 9, MakeInstructionDescriptor(9, SpvOpReturn, 0), 101); + TransformationCopyObject transformation3( + 100, MakeInstructionDescriptor(9, SpvOpReturn, 0), 102); + + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); + transformation3.Apply(context.get(), &fact_manager); + + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(8)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(9)); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp new file mode 100644 index 0000000000..1f728ffcfa --- /dev/null +++ b/test/fuzz/transformation_load_test.cpp @@ -0,0 +1,277 @@ +// 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_load.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationLoadTest, BasicTest) { + 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 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + OpReturnValue %21 + 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; + + fact_manager.AddFactValueOfPointeeIsIrrelevant(27); + fact_manager.AddFactValueOfPointeeIsIrrelevant(11); + fact_manager.AddFactValueOfPointeeIsIrrelevant(46); + fact_manager.AddFactValueOfPointeeIsIrrelevant(16); + fact_manager.AddFactValueOfPointeeIsIrrelevant(52); + + fact_manager.AddFactBlockIsDead(36); + + // Variables with pointee types: + // 52 - ptr_to(7) + // 53 - ptr_to(6) + // 20 - ptr_to(8) + // 27 - ptr_to(8) - irrelevant + + // Access chains with pointee type: + // 22 - ptr_to(6) + // 26 - ptr_to(6) + // 30 - ptr_to(6) + // 33 - ptr_to(6) + // 38 - ptr_to(6) + // 40 - ptr_to(6) + // 43 - ptr_to(6) + // 16 - ptr_to(6) - irrelevant + + // Copied object with pointee type: + // 44 - ptr_to(8) + // 45 - ptr_to(6) + // 46 - ptr_to(8) - irrelevant + + // Function parameters with pointee type: + // 11 - ptr_to(8) - irrelevant + + // Pointers that cannot be used: + // 60 - null + // 61 - undefined + + // Bad: id is not fresh + ASSERT_FALSE(TransformationLoad( + 33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + // Bad: attempt to load from 11 from outside its function + ASSERT_FALSE(TransformationLoad( + 100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer is not available + ASSERT_FALSE(TransformationLoad( + 100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to insert before OpVariable + ASSERT_FALSE(TransformationLoad( + 100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id does not exist + ASSERT_FALSE( + TransformationLoad(100, 1000, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id exists but does not have a type + ASSERT_FALSE(TransformationLoad( + 100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id exists and has a type, but is not a pointer + ASSERT_FALSE(TransformationLoad( + 100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to load from null pointer + ASSERT_FALSE(TransformationLoad( + 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to load from undefined pointer + ASSERT_FALSE(TransformationLoad( + 100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + // Bad: %40 is not available at the program point + ASSERT_FALSE( + TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: The described instruction does not exist + ASSERT_FALSE(TransformationLoad( + 100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + .IsApplicable(context.get(), fact_manager)); + + { + TransformationLoad transformation( + 100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + { + TransformationLoad transformation( + 101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + { + TransformationLoad transformation( + 102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + { + TransformationLoad transformation( + 103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); + 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %100 = OpLoad %6 %33 + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %103 = OpLoad %6 %40 + %43 = OpAccessChain %15 %20 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + %101 = OpLoad %8 %46 + %102 = OpLoad %6 %16 + OpReturnValue %21 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index 7313538b09..40aaebc9a7 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -1827,11 +1827,11 @@ TEST(TransformationOutlineFunctionTest, } TEST(TransformationOutlineFunctionTest, OutlineLivesafe) { - // In the following, %30 is a livesafe function, with arbitrary parameter - // %200 and arbitrary local variable %201. Variable %100 is a loop limiter, - // which is not arbitrary. The test checks that the outlined function is + // In the following, %30 is a livesafe function, with irrelevant parameter + // %200 and irrelevant local variable %201. Variable %100 is a loop limiter, + // which is not irrelevant. The test checks that the outlined function is // livesafe, and that the parameters corresponding to %200 and %201 have the - // arbitrary fact associated with them. + // irrelevant fact associated with them. std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -1914,8 +1914,8 @@ TEST(TransformationOutlineFunctionTest, OutlineLivesafe) { FactManager fact_manager; fact_manager.AddFactFunctionIsLivesafe(30); - fact_manager.AddFactValueOfVariableIsArbitrary(200); - fact_manager.AddFactValueOfVariableIsArbitrary(201); + fact_manager.AddFactValueOfPointeeIsIrrelevant(200); + fact_manager.AddFactValueOfPointeeIsIrrelevant(201); TransformationOutlineFunction transformation( /*entry_block*/ 198, @@ -1937,16 +1937,16 @@ TEST(TransformationOutlineFunctionTest, OutlineLivesafe) { ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30)); // The outlined function should be livesafe. ASSERT_TRUE(fact_manager.FunctionIsLivesafe(402)); - // The variable and parameter that were originally arbitrary should still be. - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(200)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(201)); - // The loop limiter should still be non-arbitrary. - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(100)); - // The parameters for the original arbitrary variables should be arbitrary. - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(408)); - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(409)); - // The parameter for the loop limiter should not be arbitrary. - ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(407)); + // The variable and parameter that were originally irrelevant should still be. + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(200)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(201)); + // The loop limiter should still be non-irrelevant. + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100)); + // The parameters for the original irrelevant variables should be irrelevant. + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(408)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(409)); + // The parameter for the loop limiter should not be irrelevant. + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(407)); std::string after_transformation = R"( OpCapability Shader @@ -2237,9 +2237,9 @@ TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks2) { } TEST(TransformationOutlineFunctionTest, - OutlineWithArbitraryVariablesAndParameters) { - // This checks that if the outlined region uses a mixture of arbitrary and - // non-arbitrary variables and parameters, these properties are preserved + OutlineWithIrrelevantVariablesAndParameters) { + // This checks that if the outlined region uses a mixture of irrelevant and + // non-irrelevant variables and parameters, these properties are preserved // during outlining. std::string shader = R"( OpCapability Shader @@ -2287,8 +2287,8 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - fact_manager.AddFactValueOfVariableIsArbitrary(9); - fact_manager.AddFactValueOfVariableIsArbitrary(14); + fact_manager.AddFactValueOfPointeeIsIrrelevant(9); + fact_manager.AddFactValueOfPointeeIsIrrelevant(14); TransformationOutlineFunction transformation( /*entry_block*/ 50, @@ -2305,10 +2305,10 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); transformation.Apply(context.get(), &fact_manager); ASSERT_TRUE(IsValid(env, context.get())); - // The variables that were originally abitrary, plus input parameters - // corresponding to them, should be arbitrary. The rest should not be. + // The variables that were originally irrelevant, plus input parameters + // corresponding to them, should be irrelevant. The rest should not be. for (uint32_t variable_id : {9u, 14u, 206u, 208u}) { - ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(variable_id)); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(variable_id)); } for (uint32_t variable_id : {10u, 20u, 207u, 209u}) { ASSERT_FALSE(fact_manager.BlockIsDead(variable_id)); diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp new file mode 100644 index 0000000000..3fb9b6154d --- /dev/null +++ b/test/fuzz/transformation_store_test.cpp @@ -0,0 +1,341 @@ +// 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_store.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationStoreTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 ; irrelevant + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 ; irrelevant + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + 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; + + fact_manager.AddFactValueOfPointeeIsIrrelevant(27); + fact_manager.AddFactValueOfPointeeIsIrrelevant(11); + fact_manager.AddFactValueOfPointeeIsIrrelevant(46); + fact_manager.AddFactValueOfPointeeIsIrrelevant(16); + fact_manager.AddFactValueOfPointeeIsIrrelevant(52); + fact_manager.AddFactValueOfPointeeIsIrrelevant(81); + fact_manager.AddFactValueOfPointeeIsIrrelevant(82); + + fact_manager.AddFactBlockIsDead(36); + + // Variables with pointee types: + // 52 - ptr_to(7) + // 53 - ptr_to(6) + // 20 - ptr_to(8) + // 27 - ptr_to(8) - irrelevant + // 92 - ptr_to(90) - read only + + // Access chains with pointee type: + // 22 - ptr_to(6) + // 26 - ptr_to(6) + // 30 - ptr_to(6) + // 33 - ptr_to(6) + // 38 - ptr_to(6) + // 40 - ptr_to(6) + // 43 - ptr_to(6) + // 16 - ptr_to(6) - irrelevant + + // Copied object with pointee type: + // 44 - ptr_to(8) + // 45 - ptr_to(6) + // 46 - ptr_to(8) - irrelevant + // 81 - ptr_to(8) - irrelevant + // 82 - ptr_to(8) - irrelevant + + // Function parameters with pointee type: + // 11 - ptr_to(8) - irrelevant + + // Pointers that cannot be used: + // 60 - null + // 61 - undefined + + // Bad: attempt to store to 11 from outside its function + ASSERT_FALSE(TransformationStore( + 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer is not available + ASSERT_FALSE(TransformationStore( + 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to insert before OpVariable + ASSERT_FALSE(TransformationStore( + 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id does not exist + ASSERT_FALSE(TransformationStore( + 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id exists but does not have a type + ASSERT_FALSE(TransformationStore( + 5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id exists and has a type, but is not a pointer + ASSERT_FALSE(TransformationStore( + 24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to store to a null pointer + ASSERT_FALSE(TransformationStore( + 60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to store to an undefined pointer + ASSERT_FALSE(TransformationStore( + 61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: %82 is not available at the program point + ASSERT_FALSE( + TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: value id does not exist + ASSERT_FALSE(TransformationStore( + 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: value id exists but does not have a type + ASSERT_FALSE(TransformationStore( + 27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: value id exists but has the wrong type + ASSERT_FALSE(TransformationStore( + 27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to store to read-only variable + ASSERT_FALSE(TransformationStore( + 92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: value is not available + ASSERT_FALSE(TransformationStore( + 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: variable being stored to does not have an irrelevant pointee value, + // and the store is not in a dead block. + ASSERT_FALSE(TransformationStore( + 20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), fact_manager)); + + // The described instruction does not exist. + ASSERT_FALSE(TransformationStore( + 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + { + // Store to irrelevant variable from dead block. + TransformationStore transformation( + 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + { + // Store to irrelevant variable from live block. + TransformationStore transformation( + 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + { + // Store to irrelevant variable from live block. + TransformationStore transformation( + 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + { + // Store to irrelevant variable from live block. + TransformationStore transformation( + 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + { + // Store to non-irrelevant variable from dead block. + TransformationStore transformation( + 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 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" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 ; irrelevant + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + OpStore %27 %80 + OpStore %53 %21 + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 ; irrelevant + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + %95 = OpCopyObject %8 %80 + OpStore %11 %95 + OpStore %46 %80 + OpStore %16 %21 + OpReturnValue %21 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools From 3d4a0dd48f1475ddb9d876623c1d643a14bf7de4 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 10 Feb 2020 20:10:41 +0000 Subject: [PATCH 18/88] spirv-fuzz: Ensure that donated variables are always initialized (#3181) This change ensures that global and local variables donated from other modules are always initialized at their declaration in the module being transformed. This is to help limit issues related to undefined behaviour that might arise due to accessing uninitialized memory. The change also introduces some helper functions in fuzzer_util to make it easier to find the pointee types of pointer types. --- source/fuzz/fuzzer_pass_donate_modules.cpp | 126 ++++++++++-------- source/fuzz/fuzzer_util.cpp | 30 +++++ source/fuzz/fuzzer_util.h | 23 ++++ source/fuzz/protobufs/spvtoolsfuzz.proto | 2 +- .../transformation_add_global_variable.cpp | 28 ++-- source/fuzz/transformation_load.cpp | 7 +- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 55 ++++---- ...ransformation_add_global_variable_test.cpp | 20 +-- 8 files changed, 184 insertions(+), 107 deletions(-) diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 83cb18cffe..e820f2509c 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -402,14 +402,22 @@ void FuzzerPassDonateModules::HandleTypesAndValues( // way they wish, and pass them as pointer parameters to functions // without worrying about whether their data might get modified. new_result_id = GetFuzzerContext()->GetFreshId(); + uint32_t remapped_pointer_type = + original_id_to_donated_id->at(type_or_value.type_id()); + uint32_t initializer_id; + if (type_or_value.NumInOperands() == 1) { + // The variable did not have an initializer; initialize it to zero. + // This is to limit problems associated with uninitialized data. + initializer_id = FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), remapped_pointer_type)); + } else { + // The variable already had an initializer; use its remapped id. + initializer_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(1)); + } ApplyTransformation(TransformationAddGlobalVariable( - new_result_id, - original_id_to_donated_id->at(type_or_value.type_id()), - type_or_value.NumInOperands() == 1 - ? 0 - : original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(1)), - true)); + new_result_id, remapped_pointer_type, initializer_id, true)); } break; case SpvOpUndef: { // It is fine to have multiple Undef instructions of the same type, so @@ -473,53 +481,65 @@ void FuzzerPassDonateModules::HandleFunctions( }); // Consider every instruction of the donor function. - function_to_donate->ForEachInst( - [&donated_instructions, - &original_id_to_donated_id](const opt::Instruction* instruction) { - // Get the instruction's input operands into donation-ready form, - // remapping any id uses in the process. - opt::Instruction::OperandList input_operands; - - // Consider each input operand in turn. - for (uint32_t in_operand_index = 0; - in_operand_index < instruction->NumInOperands(); - in_operand_index++) { - std::vector operand_data; - const opt::Operand& in_operand = - instruction->GetInOperand(in_operand_index); - switch (in_operand.type) { - case SPV_OPERAND_TYPE_ID: - case SPV_OPERAND_TYPE_TYPE_ID: - case SPV_OPERAND_TYPE_RESULT_ID: - case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: - case SPV_OPERAND_TYPE_SCOPE_ID: - // This is an id operand - it consists of a single word of data, - // which needs to be remapped so that it is replaced with the - // donated form of the id. - operand_data.push_back( - original_id_to_donated_id->at(in_operand.words[0])); - break; - default: - // For non-id operands, we just add each of the data words. - for (auto word : in_operand.words) { - operand_data.push_back(word); - } - break; + function_to_donate->ForEachInst([this, &donated_instructions, + &original_id_to_donated_id]( + const opt::Instruction* instruction) { + // Get the instruction's input operands into donation-ready form, + // remapping any id uses in the process. + opt::Instruction::OperandList input_operands; + + // Consider each input operand in turn. + for (uint32_t in_operand_index = 0; + in_operand_index < instruction->NumInOperands(); + in_operand_index++) { + std::vector operand_data; + const opt::Operand& in_operand = + instruction->GetInOperand(in_operand_index); + switch (in_operand.type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + // This is an id operand - it consists of a single word of data, + // which needs to be remapped so that it is replaced with the + // donated form of the id. + operand_data.push_back( + original_id_to_donated_id->at(in_operand.words[0])); + break; + default: + // For non-id operands, we just add each of the data words. + for (auto word : in_operand.words) { + operand_data.push_back(word); } - input_operands.push_back({in_operand.type, operand_data}); - } - // Remap the result type and result id (if present) of the - // instruction, and turn it into a protobuf message. - donated_instructions.push_back(MakeInstructionMessage( - instruction->opcode(), - instruction->type_id() - ? original_id_to_donated_id->at(instruction->type_id()) - : 0, - instruction->result_id() - ? original_id_to_donated_id->at(instruction->result_id()) - : 0, - input_operands)); - }); + break; + } + input_operands.push_back({in_operand.type, operand_data}); + } + + if (instruction->opcode() == SpvOpVariable && + instruction->NumInOperands() == 1) { + // This is an uninitialized local variable. Initialize it to zero. + input_operands.push_back( + {SPV_OPERAND_TYPE_ID, + {FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), + original_id_to_donated_id->at(instruction->type_id())))}}); + } + + // Remap the result type and result id (if present) of the + // instruction, and turn it into a protobuf message. + donated_instructions.push_back(MakeInstructionMessage( + instruction->opcode(), + instruction->type_id() + ? original_id_to_donated_id->at(instruction->type_id()) + : 0, + instruction->result_id() + ? original_id_to_donated_id->at(instruction->result_id()) + : 0, + input_operands)); + }); if (make_livesafe) { // Various types and constants must be in place for a function to be made diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 1af3fc8bad..cb90143f70 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -471,6 +471,36 @@ bool InstructionIsFunctionParameter(opt::Instruction* instruction, return found_parameter; } +uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id) { + return context->get_def_use_mgr()->GetDef(result_id)->type_id(); +} + +uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) { + assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer && + "Precondition: |pointer_type_inst| must be OpTypePointer."); + return pointer_type_inst->GetSingleWordInOperand(1); +} + +uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id) { + return GetPointeeTypeIdFromPointerType( + context->get_def_use_mgr()->GetDef(pointer_type_id)); +} + +SpvStorageClass GetStorageClassFromPointerType( + opt::Instruction* pointer_type_inst) { + assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer && + "Precondition: |pointer_type_inst| must be OpTypePointer."); + return static_cast( + pointer_type_inst->GetSingleWordInOperand(0)); +} + +SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id) { + return GetStorageClassFromPointerType( + context->get_def_use_mgr()->GetDef(pointer_type_id)); +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 3e9c6911a1..292cd9f969 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -158,6 +158,29 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context, bool InstructionIsFunctionParameter(opt::Instruction* instruction, opt::Function* function); +// Returns the type id of the instruction defined by |result_id|, or 0 if there +// is no such result id. +uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id); + +// Given |pointer_type_inst|, which must be an OpTypePointer instruction, +// returns the id of the associated pointee type. +uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst); + +// Given |pointer_type_id|, which must be the id of a pointer type, returns the +// id of the associated pointee type. +uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id); + +// Given |pointer_type_inst|, which must be an OpTypePointer instruction, +// returns the associated storage class. +SpvStorageClass GetStorageClassFromPointerType( + opt::Instruction* pointer_type_inst); + +// Given |pointer_type_id|, which must be the id of a pointer type, returns the +// associated storage class. +SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id); + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 08af3e046b..7cd59ac78f 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -505,7 +505,7 @@ message TransformationAddGlobalVariable { // The type of the global variable uint32 type_id = 2; - // Optional initializer; 0 if there is no initializer + // Initial value of the variable uint32 initializer_id = 3; // True if and only if the behaviour of the module should not depend on the diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp index 7af5888608..e4f9f7ad6c 100644 --- a/source/fuzz/transformation_add_global_variable.cpp +++ b/source/fuzz/transformation_add_global_variable.cpp @@ -53,21 +53,19 @@ bool TransformationAddGlobalVariable::IsApplicable( if (pointer_type->storage_class() != SpvStorageClassPrivate) { return false; } - if (message_.initializer_id()) { - // The initializer id must be the id of a constant. Check this with the - // constant manager. - auto constant_id = context->get_constant_mgr()->GetConstantsFromIds( - {message_.initializer_id()}); - if (constant_id.empty()) { - return false; - } - assert(constant_id.size() == 1 && - "We asked for the constant associated with a single id; we should " - "get a single constant."); - // The type of the constant must match the pointee type of the pointer. - if (pointer_type->pointee_type() != constant_id[0]->type()) { - return false; - } + // The initializer id must be the id of a constant. Check this with the + // constant manager. + auto constant_id = context->get_constant_mgr()->GetConstantsFromIds( + {message_.initializer_id()}); + if (constant_id.empty()) { + return false; + } + assert(constant_id.size() == 1 && + "We asked for the constant associated with a single id; we should " + "get a single constant."); + // The type of the constant must match the pointee type of the pointer. + if (pointer_type->pointee_type() != constant_id[0]->type()) { + return false; } return true; } diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp index ab6b8acce8..4cba37da4e 100644 --- a/source/fuzz/transformation_load.cpp +++ b/source/fuzz/transformation_load.cpp @@ -82,11 +82,8 @@ bool TransformationLoad::IsApplicable( void TransformationLoad::Apply(opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { - uint32_t result_type = context->get_def_use_mgr() - ->GetDef(context->get_def_use_mgr() - ->GetDef(message_.pointer_id()) - ->type_id()) - ->GetSingleWordInOperand(1); + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + context, fuzzerutil::GetTypeId(context, message_.pointer_id())); fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); FindInstruction(message_.instruction_to_insert_before(), context) ->InsertBefore(MakeUnique( diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index 0d202b7a98..dc7ba3a5dc 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -314,14 +314,17 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { %100 = OpTypePointer Function %6 %101 = OpTypeStruct %6 %102 = OpTypePointer Private %101 - %103 = OpVariable %102 Private - %104 = OpConstant %12 0 - %105 = OpTypePointer Private %6 - %106 = OpTypePointer Function %12 - %107 = OpTypeStruct %12 - %108 = OpTypePointer Private %107 - %109 = OpVariable %108 Private - %110 = OpTypePointer Private %12 + %104 = OpConstant %6 0 + %105 = OpConstantComposite %101 %104 + %103 = OpVariable %102 Private %105 + %106 = OpConstant %12 0 + %107 = OpTypePointer Private %6 + %108 = OpTypePointer Function %12 + %109 = OpTypeStruct %12 + %110 = OpTypePointer Private %109 + %112 = OpConstantComposite %109 %13 + %111 = OpVariable %110 Private %112 + %113 = OpTypePointer Private %12 %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function @@ -334,16 +337,16 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { OpStore %18 %24 OpReturn OpFunctionEnd - %111 = OpFunction %2 None %3 - %112 = OpLabel - %113 = OpVariable %100 Function - %114 = OpVariable %106 Function - %115 = OpAccessChain %105 %103 %104 - %116 = OpLoad %6 %115 - OpStore %113 %116 - %117 = OpAccessChain %110 %109 %104 - %118 = OpLoad %12 %117 - OpStore %114 %118 + %114 = OpFunction %2 None %3 + %115 = OpLabel + %116 = OpVariable %100 Function %104 + %117 = OpVariable %108 Function %13 + %118 = OpAccessChain %107 %103 %106 + %119 = OpLoad %6 %118 + OpStore %116 %119 + %120 = OpAccessChain %113 %111 %106 + %121 = OpLoad %12 %120 + OpStore %117 %121 OpReturn OpFunctionEnd )"; @@ -420,19 +423,21 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { %10 = OpTypePointer Input %7 %11 = OpVariable %10 Input %100 = OpTypePointer Private %7 - %101 = OpVariable %100 Private - %102 = OpTypePointer Private %7 - %103 = OpVariable %102 Private + %102 = OpConstant %6 0 + %103 = OpConstantComposite %7 %102 %102 %102 %102 + %101 = OpVariable %100 Private %103 + %104 = OpTypePointer Private %7 + %105 = OpVariable %104 Private %103 %4 = OpFunction %2 None %3 %5 = OpLabel %12 = OpLoad %7 %11 OpStore %9 %12 OpReturn OpFunctionEnd - %104 = OpFunction %2 None %3 - %105 = OpLabel - %106 = OpLoad %7 %103 - OpStore %101 %106 + %106 = OpFunction %2 None %3 + %107 = OpLabel + %108 = OpLoad %7 %105 + OpStore %101 %108 OpReturn OpFunctionEnd )"; diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp index d43a2ae362..619f068fd2 100644 --- a/test/fuzz/transformation_add_global_variable_test.cpp +++ b/test/fuzz/transformation_add_global_variable_test.cpp @@ -30,8 +30,10 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 + %40 = OpConstant %6 0 %7 = OpTypeInt 32 1 %8 = OpTypeVector %6 2 + %41 = OpConstantComposite %8 %40 %40 %9 = OpTypePointer Function %6 %10 = OpTypePointer Private %6 %20 = OpTypePointer Uniform %6 @@ -104,13 +106,13 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, 0, true), + TransformationAddGlobalVariable(100, 12, 16, true), // %101 = OpVariable %10 Private - TransformationAddGlobalVariable(101, 10, 0, false), + TransformationAddGlobalVariable(101, 10, 40, false), // %102 = OpVariable %13 Private - TransformationAddGlobalVariable(102, 13, 0, true), + TransformationAddGlobalVariable(102, 13, 41, true), // %103 = OpVariable %12 Private %16 TransformationAddGlobalVariable(103, 12, 16, false), @@ -144,8 +146,10 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 + %40 = OpConstant %6 0 %7 = OpTypeInt 32 1 %8 = OpTypeVector %6 2 + %41 = OpConstantComposite %8 %40 %40 %9 = OpTypePointer Function %6 %10 = OpTypePointer Private %6 %20 = OpTypePointer Uniform %6 @@ -160,9 +164,9 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { %19 = OpTypePointer Private %18 %21 = OpConstantTrue %18 %22 = OpConstantFalse %18 - %100 = OpVariable %12 Private - %101 = OpVariable %10 Private - %102 = OpVariable %13 Private + %100 = OpVariable %12 Private %16 + %101 = OpVariable %10 Private %40 + %102 = OpVariable %13 Private %41 %103 = OpVariable %12 Private %16 %104 = OpVariable %19 Private %21 %105 = OpVariable %19 Private %22 @@ -222,7 +226,7 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, 0, true), + TransformationAddGlobalVariable(100, 12, 16, true), // %101 = OpVariable %12 Private %16 TransformationAddGlobalVariable(101, 12, 16, false), @@ -265,7 +269,7 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { %18 = OpTypeBool %19 = OpTypePointer Private %18 %21 = OpConstantTrue %18 - %100 = OpVariable %12 Private + %100 = OpVariable %12 Private %16 %101 = OpVariable %12 Private %16 %102 = OpVariable %19 Private %21 %4 = OpFunction %2 None %3 From 77fb303e58e9d3ed08a0e72739373a58d6f07be3 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 10 Feb 2020 23:22:34 +0000 Subject: [PATCH 19/88] spirv-fuzz: Fuzzer pass to add function calls (#3178) Adds a fuzzer pass that inserts function calls into the module at random. Calls from dead blocks can be arbitrary (so long as they do not introduce recursion), while calls from other blocks can only be to livesafe functions. The change fixes some oversights in transformations to replace constants with uniforms and to obfuscate constants which testing of this fuzzer pass identified. --- source/fuzz/CMakeLists.txt | 6 + source/fuzz/call_graph.cpp | 81 ++++ source/fuzz/call_graph.h | 62 +++ source/fuzz/fuzzer.cpp | 4 + source/fuzz/fuzzer_context.cpp | 3 + source/fuzz/fuzzer_context.h | 14 +- source/fuzz/fuzzer_pass.cpp | 20 +- source/fuzz/fuzzer_pass.h | 8 +- .../fuzz/fuzzer_pass_add_function_calls.cpp | 247 ++++++++++ source/fuzz/fuzzer_pass_add_function_calls.h | 58 +++ source/fuzz/fuzzer_pass_add_loads.cpp | 2 +- source/fuzz/fuzzer_pass_add_stores.cpp | 2 +- .../fuzz/fuzzer_pass_construct_composites.cpp | 2 +- source/fuzz/fuzzer_pass_copy_objects.cpp | 2 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 52 +- .../fuzz/fuzzer_pass_obfuscate_constants.cpp | 29 +- source/fuzz/protobufs/spvtoolsfuzz.proto | 24 + source/fuzz/transformation.cpp | 3 + source/fuzz/transformation_add_function.cpp | 22 +- source/fuzz/transformation_function_call.cpp | 195 ++++++++ source/fuzz/transformation_function_call.h | 69 +++ ..._boolean_constant_with_constant_binary.cpp | 21 +- ...ormation_replace_constant_with_uniform.cpp | 6 + test/fuzz/CMakeLists.txt | 1 + .../transformation_function_call_test.cpp | 443 ++++++++++++++++++ ...ean_constant_with_constant_binary_test.cpp | 39 ++ ...ion_replace_constant_with_uniform_test.cpp | 50 ++ 27 files changed, 1389 insertions(+), 76 deletions(-) create mode 100644 source/fuzz/call_graph.cpp create mode 100644 source/fuzz/call_graph.h create mode 100644 source/fuzz/fuzzer_pass_add_function_calls.cpp create mode 100644 source/fuzz/fuzzer_pass_add_function_calls.h create mode 100644 source/fuzz/transformation_function_call.cpp create mode 100644 source/fuzz/transformation_function_call.h create mode 100644 test/fuzz/transformation_function_call_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 7166d5c441..330bbf04f7 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -29,6 +29,7 @@ if(SPIRV_BUILD_FUZZER) ) set(SPIRV_TOOLS_FUZZ_SOURCES + call_graph.h data_descriptor.h equivalence_relation.h fact_manager.h @@ -40,6 +41,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_blocks.h fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_dead_continues.h + fuzzer_pass_add_function_calls.h fuzzer_pass_add_global_variables.h fuzzer_pass_add_loads.h fuzzer_pass_add_local_variables.h @@ -92,6 +94,7 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_construct.h transformation_composite_extract.h transformation_copy_object.h + transformation_function_call.h transformation_load.h transformation_merge_blocks.h transformation_move_block_down.h @@ -109,6 +112,7 @@ if(SPIRV_BUILD_FUZZER) uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h + call_graph.cpp data_descriptor.cpp fact_manager.cpp force_render_red.cpp @@ -119,6 +123,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_blocks.cpp fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_dead_continues.cpp + fuzzer_pass_add_function_calls.cpp fuzzer_pass_add_global_variables.cpp fuzzer_pass_add_loads.cpp fuzzer_pass_add_local_variables.cpp @@ -170,6 +175,7 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_construct.cpp transformation_composite_extract.cpp transformation_copy_object.cpp + transformation_function_call.cpp transformation_load.cpp transformation_merge_blocks.cpp transformation_move_block_down.cpp diff --git a/source/fuzz/call_graph.cpp b/source/fuzz/call_graph.cpp new file mode 100644 index 0000000000..15416fe3e3 --- /dev/null +++ b/source/fuzz/call_graph.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/call_graph.h" + +#include + +namespace spvtools { +namespace fuzz { + +CallGraph::CallGraph(opt::IRContext* context) { + // Initialize function in-degree and call graph edges to 0 and empty. + for (auto& function : *context->module()) { + function_in_degree_[function.result_id()] = 0; + call_graph_edges_[function.result_id()] = std::set(); + } + + // Consider every function. + for (auto& function : *context->module()) { + // Avoid considering the same callee of this function multiple times by + // recording known callees. + std::set known_callees; + // Consider every function call instruction in every block. + for (auto& block : function) { + for (auto& instruction : block) { + if (instruction.opcode() != SpvOpFunctionCall) { + continue; + } + // Get the id of the function being called. + uint32_t callee = instruction.GetSingleWordInOperand(0); + if (known_callees.count(callee)) { + // We have already considered a call to this function - ignore it. + continue; + } + // Increase the callee's in-degree and add an edge to the call graph. + function_in_degree_[callee]++; + call_graph_edges_[function.result_id()].insert(callee); + // Mark the callee as 'known'. + known_callees.insert(callee); + } + } + } +} + +void CallGraph::PushDirectCallees(uint32_t function_id, + std::queue* queue) const { + for (auto callee : GetDirectCallees(function_id)) { + queue->push(callee); + } +} + +std::set CallGraph::GetIndirectCallees(uint32_t function_id) const { + std::set result; + std::queue queue; + PushDirectCallees(function_id, &queue); + + while (!queue.empty()) { + auto next = queue.front(); + queue.pop(); + if (result.count(next)) { + continue; + } + result.insert(next); + PushDirectCallees(next, &queue); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/call_graph.h b/source/fuzz/call_graph.h new file mode 100644 index 0000000000..14cd23b4cc --- /dev/null +++ b/source/fuzz/call_graph.h @@ -0,0 +1,62 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_CALL_GRAPH_H_ +#define SOURCE_FUZZ_CALL_GRAPH_H_ + +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Represents the acyclic call graph of a SPIR-V module. +class CallGraph { + public: + // Creates a call graph corresponding to the given SPIR-V module. + explicit CallGraph(opt::IRContext* context); + + // Returns a mapping from each function to its number of distinct callers. + const std::map& GetFunctionInDegree() const { + return function_in_degree_; + } + + // Returns the ids of the functions that |function_id| directly invokes. + const std::set& GetDirectCallees(uint32_t function_id) const { + return call_graph_edges_.at(function_id); + } + + // Returns the ids of the functions that |function_id| directly or indirectly + // invokes. + std::set GetIndirectCallees(uint32_t function_id) const; + + private: + // Pushes the direct callees of |function_id| on to |queue|. + void PushDirectCallees(uint32_t function_id, + std::queue* queue) const; + + // Maps each function id to the ids of its immediate callees. + std::map> call_graph_edges_; + + // For each function id, stores the number of distinct functions that call + // the function. + std::map function_in_degree_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_CALL_GRAPH_H_ diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index c66d8e5695..a427619642 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -25,6 +25,7 @@ #include "source/fuzz/fuzzer_pass_add_dead_blocks.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_function_calls.h" #include "source/fuzz/fuzzer_pass_add_global_variables.h" #include "source/fuzz/fuzzer_pass_add_loads.h" #include "source/fuzz/fuzzer_pass_add_local_variables.h" @@ -195,6 +196,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index b84227e94f..a43466138a 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -44,6 +44,7 @@ const std::pair kChanceOfAdjustingMemoryOperandsMask = {20, 90}; const std::pair kChanceOfAdjustingSelectionControl = {20, 90}; +const std::pair kChanceOfCallingFunction = {1, 10}; const std::pair kChanceOfChoosingStructTypeVsArrayType = { 20, 80}; const std::pair kChanceOfConstructingComposite = {20, 50}; @@ -112,6 +113,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask); chance_of_adjusting_selection_control_ = ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl); + chance_of_calling_function_ = + ChooseBetweenMinAndMax(kChanceOfCallingFunction); chance_of_choosing_struct_type_vs_array_type_ = ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType); chance_of_constructing_composite_ = diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 21f8a6279a..1d1245cebe 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -46,12 +46,22 @@ class FuzzerContext { // method, and which must be non-empty. Typically 'HasSizeMethod' will be an // std::vector. template - uint32_t RandomIndex(const HasSizeMethod& sequence) { + uint32_t RandomIndex(const HasSizeMethod& sequence) const { assert(sequence.size() > 0); return random_generator_->RandomUint32( static_cast(sequence.size())); } + // Selects a random index into |sequence|, removes the element at that index + // and returns it. + template + T RemoveAtRandomIndex(std::vector* sequence) const { + uint32_t index = RandomIndex(*sequence); + T result = sequence->at(index); + sequence->erase(sequence->begin() + index); + return result; + } + // Yields an id that is guaranteed not to be used in the module being fuzzed, // or to have been issued before. uint32_t GetFreshId(); @@ -98,6 +108,7 @@ class FuzzerContext { uint32_t GetChanceOfAdjustingSelectionControl() { return chance_of_adjusting_selection_control_; } + uint32_t GetChanceOfCallingFunction() { return chance_of_calling_function_; } uint32_t GetChanceOfChoosingStructTypeVsArrayType() { return chance_of_choosing_struct_type_vs_array_type_; } @@ -167,6 +178,7 @@ class FuzzerContext { uint32_t chance_of_adjusting_loop_control_; uint32_t chance_of_adjusting_memory_operands_mask_; uint32_t chance_of_adjusting_selection_control_; + uint32_t chance_of_calling_function_; uint32_t chance_of_choosing_struct_type_vs_array_type_; uint32_t chance_of_constructing_composite_; uint32_t chance_of_copying_object_; diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 40eb3bd395..1ecfa8d341 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -41,10 +41,10 @@ FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager, FuzzerPass::~FuzzerPass() = default; std::vector FuzzerPass::FindAvailableInstructions( - const opt::Function& function, opt::BasicBlock* block, - opt::BasicBlock::iterator inst_it, + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it, std::function - instruction_is_relevant) { + instruction_is_relevant) const { // 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. @@ -57,6 +57,14 @@ std::vector FuzzerPass::FindAvailableInstructions( } } + // Consider all function parameters + function->ForEachParam( + [this, &instruction_is_relevant, &result](opt::Instruction* param) { + if (instruction_is_relevant(GetIRContext(), param)) { + result.push_back(param); + } + }); + // Consider all previous instructions in this block for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it; ++prev_inst_it) { @@ -67,7 +75,7 @@ std::vector FuzzerPass::FindAvailableInstructions( // Walk the dominator tree to consider all instructions from dominating // blocks - auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function); + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function); for (auto next_dominator = dominator_analysis->ImmediateDominator(block); next_dominator != nullptr; next_dominator = @@ -83,7 +91,7 @@ std::vector FuzzerPass::FindAvailableInstructions( void FuzzerPass::MaybeAddTransformationBeforeEachInstruction( std::function< - void(const opt::Function& function, opt::BasicBlock* block, + void(opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor)> maybe_apply_transformation) { @@ -125,7 +133,7 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction( // Invoke the provided function, which might apply a transformation. maybe_apply_transformation( - function, &block, inst_it, + &function, &block, inst_it, MakeInstructionDescriptor( base, opcode, skip_count.count(opcode) ? skip_count.at(opcode) : 0)); diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 4b78f29a79..68531796ff 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -61,10 +61,10 @@ class FuzzerPass { // |instruction_is_relevant| predicate. This, for instance, could ignore all // instructions that have a particular decoration. std::vector FindAvailableInstructions( - const opt::Function& function, opt::BasicBlock* block, - opt::BasicBlock::iterator inst_it, + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it, std::function - instruction_is_relevant); + instruction_is_relevant) const; // A helper method that iterates through each instruction in each block, at // all times tracking an instruction descriptor that allows the latest @@ -84,7 +84,7 @@ class FuzzerPass { // apply it. void MaybeAddTransformationBeforeEachInstruction( std::function< - void(const opt::Function& function, opt::BasicBlock* block, + void(opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor)> maybe_apply_transformation); diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp new file mode 100644 index 0000000000..c89ae51641 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -0,0 +1,247 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_function_calls.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_function_call.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default; + +void FuzzerPassAddFunctionCalls::Apply() { + MaybeAddTransformationBeforeEachInstruction( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + // Check whether it is legitimate to insert a function call before the + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting a function call here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfCallingFunction())) { + return; + } + + // Compute the module's call graph - we don't cache it since it may + // change each time we apply a transformation. If this proves to be + // a bottleneck the call graph data structure could be made updatable. + CallGraph call_graph(GetIRContext()); + + // Gather all the non-entry point functions different from this + // function. It is important to ignore entry points as a function + // cannot be an entry point and the target of an OpFunctionCall + // instruction. We ignore this function to avoid direct recursion. + std::vector candidate_functions; + for (auto& other_function : *GetIRContext()->module()) { + if (&other_function != function && + !TransformationFunctionCall::FunctionIsEntryPoint( + GetIRContext(), other_function.result_id())) { + candidate_functions.push_back(&other_function); + } + } + + // Choose a function to call, at random, by considering candidate + // functions until a suitable one is found. + opt::Function* chosen_function = nullptr; + while (!candidate_functions.empty()) { + opt::Function* candidate_function = + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions); + if (!GetFactManager()->BlockIsDead(block->id()) && + !GetFactManager()->FunctionIsLivesafe( + candidate_function->result_id())) { + // Unless in a dead block, only livesafe functions can be invoked + continue; + } + if (call_graph.GetIndirectCallees(candidate_function->result_id()) + .count(function->result_id())) { + // Calling this function could lead to indirect recursion + continue; + } + chosen_function = candidate_function; + break; + } + + if (!chosen_function) { + // No suitable function was found to call. (This can happen, for + // instance, if the current function is the only function in the + // module.) + return; + } + + ApplyTransformation(TransformationFunctionCall( + GetFuzzerContext()->GetFreshId(), chosen_function->result_id(), + ChooseFunctionCallArguments(*chosen_function, function, block, + inst_it), + instruction_descriptor)); + }); +} + +std::map> +FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters( + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it) { + // Find all instructions in scope that could potentially be used as actual + // parameters. Weed out unsuitable pointer arguments immediately. + std::vector potentially_suitable_instructions = + FindAvailableInstructions( + function, block, inst_it, + [this, block](opt::IRContext* context, + opt::Instruction* inst) -> bool { + if (!inst->HasResultId() || !inst->type_id()) { + // An instruction needs a result id and type in order + // to be suitable as an actual parameter. + return false; + } + if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == + SpvOpTypePointer) { + switch (inst->opcode()) { + case SpvOpFunctionParameter: + case SpvOpVariable: + // Function parameters and variables are the only + // kinds of pointer that can be used as actual + // parameters. + break; + default: + return false; + } + if (!GetFactManager()->BlockIsDead(block->id()) && + !GetFactManager()->PointeeValueIsIrrelevant( + inst->result_id())) { + // We can only pass a pointer as an actual parameter + // if the pointee value for the pointer is irrelevant, + // or if the block from which we would make the + // function call is dead. + return false; + } + } + return true; + }); + + // Group all the instructions that are potentially viable as function actual + // parameters by their result types. + std::map> result; + for (auto inst : potentially_suitable_instructions) { + if (result.count(inst->type_id()) == 0) { + // This is the first instruction of this type we have seen, so populate + // the map with an entry. + result.insert({inst->type_id(), {}}); + } + // Add the instruction to the sequence of instructions already associated + // with this type. + result.at(inst->type_id()).push_back(inst); + } + return result; +} + +std::vector FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments( + const opt::Function& callee, opt::Function* caller_function, + opt::BasicBlock* caller_block, + const opt::BasicBlock::iterator& caller_inst_it) { + auto type_to_available_instructions = + GetAvailableInstructionsSuitableForActualParameters( + caller_function, caller_block, caller_inst_it); + + opt::Instruction* function_type = GetIRContext()->get_def_use_mgr()->GetDef( + callee.DefInst().GetSingleWordInOperand(1)); + assert(function_type->opcode() == SpvOpTypeFunction && + "The function type does not have the expected opcode."); + std::vector result; + for (uint32_t arg_index = 1; arg_index < function_type->NumInOperands(); + arg_index++) { + auto arg_type_id = + GetIRContext() + ->get_def_use_mgr() + ->GetDef(function_type->GetSingleWordInOperand(arg_index)) + ->result_id(); + if (type_to_available_instructions.count(arg_type_id)) { + std::vector& candidate_arguments = + type_to_available_instructions.at(arg_type_id); + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177) The value + // selected here is arbitrary. We should consider adding this + // information as a fact so that the passed parameter could be + // transformed/changed. + result.push_back(candidate_arguments[GetFuzzerContext()->RandomIndex( + candidate_arguments)] + ->result_id()); + } else { + // We don't have a suitable id in scope to pass, so we must make + // something up. + auto type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(arg_type_id); + + if (type_instruction->opcode() == SpvOpTypePointer) { + // In the case of a pointer, we make a new variable, at function + // or global scope depending on the storage class of the + // pointer. + + // Get a fresh id for the new variable. + uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId(); + + // The id of this variable is what we pass as the parameter to + // the call. + result.push_back(fresh_variable_id); + + // Now bring the variable into existence. + if (type_instruction->GetSingleWordInOperand(0) == + SpvStorageClassFunction) { + // Add a new zero-initialized local variable to the current + // function, noting that its pointee value is irrelevant. + ApplyTransformation(TransformationAddLocalVariable( + fresh_variable_id, arg_type_id, caller_function->result_id(), + FindOrCreateZeroConstant( + type_instruction->GetSingleWordInOperand(1)), + true)); + } else { + assert(type_instruction->GetSingleWordInOperand(0) == + SpvStorageClassPrivate && + "Only Function and Private storage classes are " + "supported at present."); + // Add a new zero-initialized global variable to the module, + // noting that its pointee value is irrelevant. + ApplyTransformation(TransformationAddGlobalVariable( + fresh_variable_id, arg_type_id, + FindOrCreateZeroConstant( + type_instruction->GetSingleWordInOperand(1)), + true)); + } + } else { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): We use + // constant zero for the parameter, but could consider adding a fact + // to allow further passes to obfuscate it. + result.push_back(FindOrCreateZeroConstant(arg_type_id)); + } + } + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h new file mode 100644 index 0000000000..5d184fd168 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_function_calls.h @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds calls at random to (a) livesafe functions, from +// anywhere, and (b) any functions, from dead blocks. +class FuzzerPassAddFunctionCalls : public FuzzerPass { + public: + FuzzerPassAddFunctionCalls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddFunctionCalls(); + + void Apply() override; + + private: + // Identify all instructions available at |instr_it|, in block |block| of + // |function|, that are potentially suitable as function call actual + // parameters. The results are grouped by type. + std::map> + GetAvailableInstructionsSuitableForActualParameters( + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it); + + // Randomly chooses suitable arguments to invoke |callee| right before + // instruction |caller_inst_it| of block |caller_block| in |caller_function|, + // based on both existing available instructions and the addition of new + // instructions to the module. + std::vector ChooseFunctionCallArguments( + const opt::Function& callee, opt::Function* caller_function, + opt::BasicBlock* caller_block, + const opt::BasicBlock::iterator& caller_inst_it); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index 7509bce4bf..2fe1220e11 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -30,7 +30,7 @@ FuzzerPassAddLoads::~FuzzerPassAddLoads() = default; void FuzzerPassAddLoads::Apply() { MaybeAddTransformationBeforeEachInstruction( - [this](const opt::Function& function, opt::BasicBlock* block, + [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) -> void { diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp index 120c473226..d2c7b3df2d 100644 --- a/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -30,7 +30,7 @@ FuzzerPassAddStores::~FuzzerPassAddStores() = default; void FuzzerPassAddStores::Apply() { MaybeAddTransformationBeforeEachInstruction( - [this](const opt::Function& function, opt::BasicBlock* block, + [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) -> void { diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index ff0adabcc3..e160302162 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -44,7 +44,7 @@ void FuzzerPassConstructComposites::Apply() { MaybeAddTransformationBeforeEachInstruction( [this, &composite_type_ids]( - const opt::Function& function, opt::BasicBlock* block, + opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) -> void { diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp index 48ed58854f..0fbe5cbc8c 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -30,7 +30,7 @@ FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default; void FuzzerPassCopyObjects::Apply() { MaybeAddTransformationBeforeEachInstruction( - [this](const opt::Function& function, opt::BasicBlock* block, + [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) -> void { diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index e820f2509c..3368665852 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -18,6 +18,7 @@ #include #include +#include "source/fuzz/call_graph.h" #include "source/fuzz/instruction_message.h" #include "source/fuzz/transformation_add_constant_boolean.h" #include "source/fuzz/transformation_add_constant_composite.h" @@ -686,53 +687,18 @@ void FuzzerPassDonateModules::HandleFunctions( std::vector FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder( opt::IRContext* context) { - // This is an implementation of Kahn’s algorithm for topological sorting. - - // For each function id, stores the number of distinct functions that call - // the function. - std::map function_in_degree; - - // We first build a call graph for the module, and compute the in-degree for - // each function in the process. - // TODO(afd): If there is functionality elsewhere in the SPIR-V tools - // framework to construct call graphs it could be nice to re-use it here. - std::map> call_graph_edges; + CallGraph call_graph(context); - // Initialize function in-degree and call graph edges to 0 and empty. - for (auto& function : *context->module()) { - function_in_degree[function.result_id()] = 0; - call_graph_edges[function.result_id()] = std::set(); - } - - // Consider every function. - for (auto& function : *context->module()) { - // Avoid considering the same callee of this function multiple times by - // recording known callees. - std::set known_callees; - // Consider every function call instruction in every block. - for (auto& block : function) { - for (auto& instruction : block) { - if (instruction.opcode() != SpvOpFunctionCall) { - continue; - } - // Get the id of the function being called. - uint32_t callee = instruction.GetSingleWordInOperand(0); - if (known_callees.count(callee)) { - // We have already considered a call to this function - ignore it. - continue; - } - // Increase the callee's in-degree and add an edge to the call graph. - function_in_degree[callee]++; - call_graph_edges[function.result_id()].insert(callee); - // Mark the callee as 'known'. - known_callees.insert(callee); - } - } - } + // This is an implementation of Kahn’s algorithm for topological sorting. // This is the sorted order of function ids that we will eventually return. std::vector result; + // Get a copy of the initial in-degrees of all functions. The algorithm + // involves decrementing these values, hence why we work on a copy. + std::map function_in_degree = + call_graph.GetFunctionInDegree(); + // Populate a queue with all those function ids with in-degree zero. std::queue queue; for (auto& entry : function_in_degree) { @@ -748,7 +714,7 @@ FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder( auto next = queue.front(); queue.pop(); result.push_back(next); - for (auto successor : call_graph_edges.at(next)) { + for (auto successor : call_graph.GetDirectCallees(next)) { assert(function_in_degree.at(successor) > 0 && "The in-degree cannot be zero if the function is a successor."); function_in_degree[successor] = function_in_degree.at(successor) - 1; diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index 3df11aeecd..2caf0c6b65 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -416,13 +416,28 @@ void FuzzerPassObfuscateConstants::Apply() { skipped_opcode_count.clear(); } - // Consider each operand of the instruction, and add a constant id use - // for the operand if relevant. - for (uint32_t in_operand_index = 0; - in_operand_index < inst.NumInOperands(); in_operand_index++) { - MaybeAddConstantIdUse(inst, in_operand_index, - base_instruction_result_id, - skipped_opcode_count, &constant_uses); + switch (inst.opcode()) { + case SpvOpPhi: + // The instruction must not be an OpPhi, as we cannot insert + // instructions before an OpPhi. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): + // there is scope for being less conservative. + break; + case SpvOpVariable: + // The instruction must not be an OpVariable, the only id that an + // OpVariable uses is an initializer id, which has to remain + // constant. + break; + default: + // Consider each operand of the instruction, and add a constant id + // use for the operand if relevant. + for (uint32_t in_operand_index = 0; + in_operand_index < inst.NumInOperands(); in_operand_index++) { + MaybeAddConstantIdUse(inst, in_operand_index, + base_instruction_result_id, + skipped_opcode_count, &constant_uses); + } + break; } if (!inst.HasResultId()) { diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 7cd59ac78f..8a931ba083 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -340,6 +340,7 @@ message Transformation { TransformationAddLocalVariable add_local_variable = 35; TransformationLoad load = 36; TransformationStore store = 37; + TransformationFunctionCall function_call = 38; // Add additional option using the next available number. } } @@ -731,6 +732,29 @@ message TransformationCopyObject { } +message TransformationFunctionCall { + + // A transformation that introduces an OpFunctionCall instruction. The call + // must not make the module's call graph cyclic. Beyond that, if the call + // is in a dead block it can be to any function with arbitrary suitably-typed + // arguments; otherwise it must be to a livesafe function, with injected + // variables as pointer arguments and arbitrary non-pointer arguments. + + // A fresh id for the result of the call + uint32 fresh_id = 1; + + // Id of the function to be called + uint32 callee_id = 2; + + // Ids for arguments to the function + repeated uint32 argument_id = 3; + + // A descriptor for an instruction in a block before which the new + // OpFunctionCall instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + +} + message TransformationLoad { // Transformation that adds an OpLoad instruction from a pointer into an id. diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index 7d32dc0640..d41a7345ca 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -40,6 +40,7 @@ #include "source/fuzz/transformation_composite_construct.h" #include "source/fuzz/transformation_composite_extract.h" #include "source/fuzz/transformation_copy_object.h" +#include "source/fuzz/transformation_function_call.h" #include "source/fuzz/transformation_load.h" #include "source/fuzz/transformation_merge_blocks.h" #include "source/fuzz/transformation_move_block_down.h" @@ -124,6 +125,8 @@ std::unique_ptr Transformation::FromMessage( message.composite_extract()); case protobufs::Transformation::TransformationCase::kCopyObject: return MakeUnique(message.copy_object()); + case protobufs::Transformation::TransformationCase::kFunctionCall: + return MakeUnique(message.function_call()); case protobufs::Transformation::TransformationCase::kLoad: return MakeUnique(message.load()); case protobufs::Transformation::TransformationCase::kMergeBlocks: diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp index 120b3df9f3..8f0d3c9207 100644 --- a/source/fuzz/transformation_add_function.cpp +++ b/source/fuzz/transformation_add_function.cpp @@ -132,17 +132,27 @@ bool TransformationAddFunction::IsApplicable( return false; } + // Check whether the cloned module is still valid after adding the function. + // If it is not, the transformation is not applicable. + if (!fuzzerutil::IsValid(cloned_module.get())) { + return false; + } + if (message_.is_livesafe()) { - // We make the cloned module livesafe. if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) { return false; } + // After making the function livesafe, we check validity of the module + // again. This is because the turning of OpKill, OpUnreachable and OpReturn + // instructions into branches changes control flow graph reachability, which + // has the potential to make the module invalid when it was otherwise valid. + // It is simpler to rely on the validator to guard against this than to + // consider all scenarios when making a function livesafe. + if (!fuzzerutil::IsValid(cloned_module.get())) { + return false; + } } - - // Having managed to add the new function to the cloned module, and - // potentially also made it livesafe, we ascertain whether the cloned module - // is still valid. If it is, the transformation is applicable. - return fuzzerutil::IsValid(cloned_module.get()); + return true; } void TransformationAddFunction::Apply( diff --git a/source/fuzz/transformation_function_call.cpp b/source/fuzz/transformation_function_call.cpp new file mode 100644 index 0000000000..69886640dc --- /dev/null +++ b/source/fuzz/transformation_function_call.cpp @@ -0,0 +1,195 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_function_call.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationFunctionCall::TransformationFunctionCall( + const spvtools::fuzz::protobufs::TransformationFunctionCall& message) + : message_(message) {} + +TransformationFunctionCall::TransformationFunctionCall( + uint32_t fresh_id, uint32_t callee_id, + const std::vector& argument_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_callee_id(callee_id); + for (auto argument : argument_id) { + message_.add_argument_id(argument); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationFunctionCall::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& fact_manager) const { + // The result id must be fresh + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + + // The function must exist + auto callee_inst = context->get_def_use_mgr()->GetDef(message_.callee_id()); + if (!callee_inst || callee_inst->opcode() != SpvOpFunction) { + return false; + } + + // The function must not be an entry point + if (FunctionIsEntryPoint(context, message_.callee_id())) { + return false; + } + + auto callee_type_inst = context->get_def_use_mgr()->GetDef( + callee_inst->GetSingleWordInOperand(1)); + assert(callee_type_inst->opcode() == SpvOpTypeFunction && + "Bad function type."); + + // The number of expected function arguments must match the number of given + // arguments. The number of expected arguments is one less than the function + // type's number of input operands, as one operand is for the return type. + if (callee_type_inst->NumInOperands() - 1 != + static_cast(message_.argument_id().size())) { + return false; + } + + // The instruction descriptor must refer to a position where it is valid to + // insert the call + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), context); + if (!insert_before) { + return false; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall, + insert_before)) { + return false; + } + + auto block = context->get_instr_block(insert_before); + auto enclosing_function = block->GetParent(); + + // If the block is not dead, the function must be livesafe + bool block_is_dead = fact_manager.BlockIsDead(block->id()); + if (!block_is_dead && + !fact_manager.FunctionIsLivesafe(message_.callee_id())) { + return false; + } + + // The ids must all match and have the right types and satisfy rules on + // pointers. If the block is not dead, pointers must be arbitrary. + for (uint32_t arg_index = 0; + arg_index < static_cast(message_.argument_id().size()); + arg_index++) { + opt::Instruction* arg_inst = + context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index)); + if (!arg_inst) { + // The given argument does not correspond to an instruction. + return false; + } + if (!arg_inst->type_id()) { + // The given argument does not have a type; it is thus not suitable. + } + if (arg_inst->type_id() != + callee_type_inst->GetSingleWordInOperand(arg_index + 1)) { + // Argument type mismatch. + return false; + } + opt::Instruction* arg_type_inst = + context->get_def_use_mgr()->GetDef(arg_inst->type_id()); + if (arg_type_inst->opcode() == SpvOpTypePointer) { + switch (arg_inst->opcode()) { + case SpvOpFunctionParameter: + case SpvOpVariable: + // These are OK + break; + default: + // Other pointer ids cannot be passed as parameters + return false; + } + if (!block_is_dead && + !fact_manager.PointeeValueIsIrrelevant(arg_inst->result_id())) { + // This is not a dead block, so pointer parameters passed to the called + // function might really have their contents modified. We thus require + // such pointers to be to arbitrary-valued variables, which this is not. + return false; + } + } + + // The argument id needs to be available (according to dominance rules) at + // the point where the call will occur. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + arg_inst->result_id())) { + return false; + } + } + + // Introducing the call must not lead to recursion. + if (message_.callee_id() == enclosing_function->result_id()) { + // This would be direct recursion. + return false; + } + // Ensure the call would not lead to indirect recursion. + return !CallGraph(context) + .GetIndirectCallees(message_.callee_id()) + .count(block->GetParent()->result_id()); +} + +void TransformationFunctionCall::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + // Update the module's bound to reflect the fresh id for the result of the + // function call. + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + // Get the return type of the function being called. + uint32_t return_type = + context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id(); + // Populate the operands to the call instruction, with the function id and the + // arguments. + opt::Instruction::OperandList operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {message_.callee_id()}}); + for (auto arg : message_.argument_id()) { + operands.push_back({SPV_OPERAND_TYPE_ID, {arg}}); + } + // Insert the function call before the instruction specified in the message. + FindInstruction(message_.instruction_to_insert_before(), context) + ->InsertBefore( + MakeUnique(context, SpvOpFunctionCall, return_type, + message_.fresh_id(), operands)); + // Invalidate all analyses since we have changed the module. + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationFunctionCall::ToMessage() const { + protobufs::Transformation result; + *result.mutable_function_call() = message_; + return result; +} + +bool TransformationFunctionCall::FunctionIsEntryPoint(opt::IRContext* context, + uint32_t function_id) { + for (auto& entry_point : context->module()->entry_points()) { + if (entry_point.GetSingleWordInOperand(1) == function_id) { + return true; + } + } + return false; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_function_call.h b/source/fuzz/transformation_function_call.h new file mode 100644 index 0000000000..e977e1dd56 --- /dev/null +++ b/source/fuzz/transformation_function_call.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_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 TransformationFunctionCall : public Transformation { + public: + explicit TransformationFunctionCall( + const protobufs::TransformationFunctionCall& message); + + TransformationFunctionCall( + uint32_t fresh_id, uint32_t callee_id, + const std::vector& argument_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh + // - |message_.instruction_to_insert_before| must identify an instruction + // before which an OpFunctionCall can be legitimately inserted + // - |message_.function_id| must be the id of a function, and calling the + // function before the identified instruction must not introduce recursion + // - |message_.arg_id| must provide suitable arguments for the function call + // (they must have the right types and be available according to dominance + // rules) + // - If the insertion point is not in a dead block then |message_function_id| + // must refer to a livesafe function, and every pointer argument in + // |message_.arg_id| must refer to an arbitrary-valued variable + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an instruction of the form: + // |fresh_id| = OpFunctionCall %type |callee_id| |arg_id...| + // before |instruction_to_insert_before|, where %type is the return type of + // |callee_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + // Helper to determine whether |function_id| is targeted by OpEntryPoint. + static bool FunctionIsEntryPoint(opt::IRContext* context, + uint32_t function_id); + + private: + protobufs::TransformationFunctionCall message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp index b097767fba..72d9b228d9 100644 --- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp +++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp @@ -243,11 +243,22 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( return false; } - // The instruction must not be an OpPhi, as we cannot insert a binary - // operator instruction before an OpPhi. - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is - // scope for being less conservative. - return instruction->opcode() != SpvOpPhi; + switch (instruction->opcode()) { + case SpvOpPhi: + // The instruction must not be an OpPhi, as we cannot insert a binary + // operator instruction before an OpPhi. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is + // scope for being less conservative. + return false; + case SpvOpVariable: + // The instruction must not be an OpVariable, because (a) we cannot insert + // a binary operator before an OpVariable, but in any case (b) the + // constant we would be replacing is the initializer constant of the + // OpVariable, and this cannot be the result of a binary operation. + return false; + default: + return true; + } } void TransformationReplaceBooleanConstantWithConstantBinary::Apply( diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp index 405776ec03..8e0e4e5a64 100644 --- a/source/fuzz/transformation_replace_constant_with_uniform.cpp +++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp @@ -154,6 +154,12 @@ bool TransformationReplaceConstantWithUniform::IsApplicable( return false; } + // The use must not be a variable initializer; these are required to be + // constants, so it would be illegal to replace one with a uniform access. + if (instruction_using_constant->opcode() == SpvOpVariable) { + return false; + } + // The module needs to have a uniform pointer type suitable for indexing into // the uniform variable, i.e. matching the type of the constant we wish to // replace with a uniform. diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 29d33ec60c..4a423a9a1c 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -47,6 +47,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_composite_construct_test.cpp transformation_composite_extract_test.cpp transformation_copy_object_test.cpp + transformation_function_call_test.cpp transformation_load_test.cpp transformation_merge_blocks_test.cpp transformation_move_block_down_test.cpp diff --git a/test/fuzz/transformation_function_call_test.cpp b/test/fuzz/transformation_function_call_test.cpp new file mode 100644 index 0000000000..9bd971e230 --- /dev/null +++ b/test/fuzz/transformation_function_call_test.cpp @@ -0,0 +1,443 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_function_call.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationFunctionCallTest, BasicTest) { + 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 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %14 = OpTypeFunction %6 %7 %13 + %27 = OpConstant %6 1 + %50 = OpConstant %12 1 + %57 = OpTypeBool + %58 = OpConstantFalse %57 + %204 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %61 = OpVariable %7 Function + %62 = OpVariable %7 Function + %65 = OpVariable %13 Function + %66 = OpVariable %7 Function + %68 = OpVariable %13 Function + %71 = OpVariable %7 Function + %72 = OpVariable %13 Function + %73 = OpVariable %7 Function + %75 = OpVariable %13 Function + %78 = OpVariable %7 Function + %98 = OpAccessChain %7 %71 + %99 = OpCopyObject %7 %71 + OpSelectionMerge %60 None + OpBranchConditional %58 %59 %60 + %59 = OpLabel + OpBranch %60 + %60 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %26 = OpLoad %6 %9 + %28 = OpIAdd %6 %26 %27 + OpSelectionMerge %97 None + OpBranchConditional %58 %96 %97 + %96 = OpLabel + OpBranch %97 + %97 = OpLabel + OpReturnValue %28 + OpFunctionEnd + %17 = OpFunction %6 None %14 + %15 = OpFunctionParameter %7 + %16 = OpFunctionParameter %13 + %18 = OpLabel + %31 = OpVariable %7 Function + %32 = OpLoad %6 %15 + OpStore %31 %32 + %33 = OpFunctionCall %6 %10 %31 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 None %14 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %13 + %22 = OpLabel + %36 = OpLoad %6 %19 + %37 = OpLoad %12 %20 + %38 = OpConvertFToS %6 %37 + %39 = OpIAdd %6 %36 %38 + OpReturnValue %39 + OpFunctionEnd + %24 = OpFunction %6 None %8 + %23 = OpFunctionParameter %7 + %25 = OpLabel + %44 = OpVariable %7 Function + %46 = OpVariable %13 Function + %51 = OpVariable %7 Function + %52 = OpVariable %13 Function + %42 = OpLoad %6 %23 + %43 = OpConvertSToF %12 %42 + %45 = OpLoad %6 %23 + OpStore %44 %45 + OpStore %46 %43 + %47 = OpFunctionCall %6 %17 %44 %46 + %48 = OpLoad %6 %23 + %49 = OpIAdd %6 %48 %27 + OpStore %51 %49 + OpStore %52 %50 + %53 = OpFunctionCall %6 %17 %51 %52 + %54 = OpIAdd %6 %47 %53 + OpReturnValue %54 + OpFunctionEnd + %200 = OpFunction %6 None %14 + %201 = OpFunctionParameter %7 + %202 = OpFunctionParameter %13 + %203 = OpLabel + OpSelectionMerge %206 None + OpBranchConditional %58 %205 %206 + %205 = OpLabel + OpBranch %206 + %206 = OpLabel + OpReturnValue %204 + 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; + + fact_manager.AddFactBlockIsDead(59); + fact_manager.AddFactBlockIsDead(11); + fact_manager.AddFactBlockIsDead(18); + fact_manager.AddFactBlockIsDead(25); + fact_manager.AddFactBlockIsDead(96); + fact_manager.AddFactBlockIsDead(205); + fact_manager.AddFactFunctionIsLivesafe(21); + fact_manager.AddFactFunctionIsLivesafe(200); + fact_manager.AddFactValueOfPointeeIsIrrelevant(71); + fact_manager.AddFactValueOfPointeeIsIrrelevant(72); + fact_manager.AddFactValueOfPointeeIsIrrelevant(19); + fact_manager.AddFactValueOfPointeeIsIrrelevant(20); + fact_manager.AddFactValueOfPointeeIsIrrelevant(23); + fact_manager.AddFactValueOfPointeeIsIrrelevant(44); + fact_manager.AddFactValueOfPointeeIsIrrelevant(46); + fact_manager.AddFactValueOfPointeeIsIrrelevant(51); + fact_manager.AddFactValueOfPointeeIsIrrelevant(52); + + // Livesafe functions with argument types: 21(7, 13), 200(7, 13) + // Non-livesafe functions with argument types: 4(), 10(7), 17(7, 13), 24(7) + // Call graph edges: + // 17 -> 10 + // 24 -> 17 + + // Bad transformations + // Too many arguments + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {71, 72, 71}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Too few arguments + ASSERT_FALSE(TransformationFunctionCall( + 100, 21, {71}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Arguments are the wrong way around (types do not match) + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {72, 71}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // 21 is not an appropriate argument + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {21, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // 300 does not exist + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {300, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // 71 is not a function + ASSERT_FALSE( + TransformationFunctionCall(100, 71, {71, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // 500 does not exist + ASSERT_FALSE( + TransformationFunctionCall(100, 500, {71, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Id is not fresh + ASSERT_FALSE( + TransformationFunctionCall(21, 21, {71, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Access chain as pointer parameter + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {98, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Copied object as pointer parameter + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {99, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Non-livesafe called from original live block + ASSERT_FALSE( + TransformationFunctionCall( + 100, 10, {71}, MakeInstructionDescriptor(99, SpvOpSelectionMerge, 0)) + .IsApplicable(context.get(), fact_manager)); + // Non-livesafe called from livesafe function + ASSERT_FALSE( + TransformationFunctionCall( + 100, 10, {19}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0)) + .IsApplicable(context.get(), fact_manager)); + // Livesafe function called with pointer to non-arbitrary local variable + ASSERT_FALSE( + TransformationFunctionCall( + 100, 21, {61, 72}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0)) + .IsApplicable(context.get(), fact_manager)); + // Direct recursion + ASSERT_FALSE(TransformationFunctionCall( + 100, 4, {}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Indirect recursion + ASSERT_FALSE(TransformationFunctionCall( + 100, 24, {9}, MakeInstructionDescriptor(96, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + // Parameter 23 is not available at the call site + ASSERT_FALSE( + TransformationFunctionCall(104, 10, {23}, + MakeInstructionDescriptor(205, SpvOpBranch, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Good transformations + { + // Livesafe called from dead block: fine + TransformationFunctionCall transformation( + 100, 21, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + { + // Livesafe called from original live block: fine + TransformationFunctionCall transformation( + 101, 21, {71, 72}, MakeInstructionDescriptor(98, SpvOpAccessChain, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + { + // Livesafe called from livesafe function: fine + TransformationFunctionCall transformation( + 102, 200, {19, 20}, MakeInstructionDescriptor(36, SpvOpLoad, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + { + // Dead called from dead block in injected function: fine + TransformationFunctionCall transformation( + 103, 10, {23}, MakeInstructionDescriptor(45, SpvOpLoad, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + { + // Non-livesafe called from dead block in livesafe function: OK + TransformationFunctionCall transformation( + 104, 10, {201}, MakeInstructionDescriptor(205, SpvOpBranch, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + { + // Livesafe called from dead block with non-arbitrary parameter + TransformationFunctionCall transformation( + 105, 21, {62, 65}, MakeInstructionDescriptor(59, SpvOpBranch, 0)); + 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %14 = OpTypeFunction %6 %7 %13 + %27 = OpConstant %6 1 + %50 = OpConstant %12 1 + %57 = OpTypeBool + %58 = OpConstantFalse %57 + %204 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %61 = OpVariable %7 Function + %62 = OpVariable %7 Function + %65 = OpVariable %13 Function + %66 = OpVariable %7 Function + %68 = OpVariable %13 Function + %71 = OpVariable %7 Function + %72 = OpVariable %13 Function + %73 = OpVariable %7 Function + %75 = OpVariable %13 Function + %78 = OpVariable %7 Function + %101 = OpFunctionCall %6 %21 %71 %72 + %98 = OpAccessChain %7 %71 + %99 = OpCopyObject %7 %71 + OpSelectionMerge %60 None + OpBranchConditional %58 %59 %60 + %59 = OpLabel + %100 = OpFunctionCall %6 %21 %71 %72 + %105 = OpFunctionCall %6 %21 %62 %65 + OpBranch %60 + %60 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %26 = OpLoad %6 %9 + %28 = OpIAdd %6 %26 %27 + OpSelectionMerge %97 None + OpBranchConditional %58 %96 %97 + %96 = OpLabel + OpBranch %97 + %97 = OpLabel + OpReturnValue %28 + OpFunctionEnd + %17 = OpFunction %6 None %14 + %15 = OpFunctionParameter %7 + %16 = OpFunctionParameter %13 + %18 = OpLabel + %31 = OpVariable %7 Function + %32 = OpLoad %6 %15 + OpStore %31 %32 + %33 = OpFunctionCall %6 %10 %31 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 None %14 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %13 + %22 = OpLabel + %102 = OpFunctionCall %6 %200 %19 %20 + %36 = OpLoad %6 %19 + %37 = OpLoad %12 %20 + %38 = OpConvertFToS %6 %37 + %39 = OpIAdd %6 %36 %38 + OpReturnValue %39 + OpFunctionEnd + %24 = OpFunction %6 None %8 + %23 = OpFunctionParameter %7 + %25 = OpLabel + %44 = OpVariable %7 Function + %46 = OpVariable %13 Function + %51 = OpVariable %7 Function + %52 = OpVariable %13 Function + %42 = OpLoad %6 %23 + %43 = OpConvertSToF %12 %42 + %103 = OpFunctionCall %6 %10 %23 + %45 = OpLoad %6 %23 + OpStore %44 %45 + OpStore %46 %43 + %47 = OpFunctionCall %6 %17 %44 %46 + %48 = OpLoad %6 %23 + %49 = OpIAdd %6 %48 %27 + OpStore %51 %49 + OpStore %52 %50 + %53 = OpFunctionCall %6 %17 %51 %52 + %54 = OpIAdd %6 %47 %53 + OpReturnValue %54 + OpFunctionEnd + %200 = OpFunction %6 None %14 + %201 = OpFunctionParameter %7 + %202 = OpFunctionParameter %13 + %203 = OpLabel + OpSelectionMerge %206 None + OpBranchConditional %58 %205 %206 + %205 = OpLabel + %104 = OpFunctionCall %6 %10 %201 + OpBranch %206 + %206 = OpLabel + OpReturnValue %204 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint) { + 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 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = 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; + + fact_manager.AddFactBlockIsDead(11); + + // 4 is an entry point, so it is not legal for it to be the target of a call. + ASSERT_FALSE(TransformationFunctionCall( + 100, 4, {}, MakeInstructionDescriptor(11, SpvOpReturn, 0)) + .IsApplicable(context.get(), fact_manager)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp index bfc7fa77cb..527a7b7d5a 100644 --- a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp +++ b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp @@ -650,6 +650,45 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) { ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager)); } +TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, + DoNotReplaceVariableInitializer) { + 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 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %13 = OpConstant %10 0 + %15 = OpConstant %10 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpVariable %7 Function %9 + 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; + + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + MakeIdUseDescriptor( + 9, MakeInstructionDescriptor(50, SpvOpVariable, 0), 1), + 13, 15, SpvOpSLessThan, 100) + .IsApplicable(context.get(), fact_manager)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp index ac2e3f9a63..58d4a89509 100644 --- a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp +++ b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp @@ -1442,6 +1442,56 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { ASSERT_TRUE(IsEqual(env, after, context.get())); } +TEST(TransformationReplaceConstantWithUniformTest, + DoNotReplaceVariableInitializer) { + // If a local variable has a constant initializer, this cannot be replaced + // by a uniform. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpMemberDecorate %16 0 Offset 0 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %16 = OpTypeStruct %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function %50 + 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; + protobufs::UniformBufferElementDescriptor blockname_a = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_a)); + + ASSERT_FALSE(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor( + 50, MakeInstructionDescriptor(8, SpvOpVariable, 0), 1), + blockname_a, 100, 101) + .IsApplicable(context.get(), fact_manager)); +} + } // namespace } // namespace fuzz } // namespace spvtools From 6c218ec60b5f6b525f1badb60c820cae20bd4df3 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 11 Feb 2020 23:10:57 +0000 Subject: [PATCH 20/88] spirv-fuzz: Fuzzer pass that adds access chains (#3182) This change adds a fuzzer pass that sprinkles access chain instructions into a module at random. This allows other passes to have a richer set of pointers available to them, in particular the passes that add loads and stores. --- source/fuzz/CMakeLists.txt | 4 + source/fuzz/fuzzer.cpp | 4 + source/fuzz/fuzzer_context.cpp | 15 +- source/fuzz/fuzzer_context.h | 15 +- source/fuzz/fuzzer_pass.cpp | 20 +- source/fuzz/fuzzer_pass.h | 6 + source/fuzz/fuzzer_pass_add_access_chains.cpp | 169 +++++++ source/fuzz/fuzzer_pass_add_access_chains.h | 41 ++ source/fuzz/fuzzer_util.cpp | 87 ++-- source/fuzz/fuzzer_util.h | 20 + source/fuzz/protobufs/spvtoolsfuzz.proto | 20 + source/fuzz/transformation.cpp | 3 + source/fuzz/transformation_access_chain.cpp | 215 +++++++++ source/fuzz/transformation_access_chain.h | 80 ++++ test/fuzz/CMakeLists.txt | 1 + .../fuzz/transformation_access_chain_test.cpp | 448 ++++++++++++++++++ 16 files changed, 1102 insertions(+), 46 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_add_access_chains.cpp create mode 100644 source/fuzz/fuzzer_pass_add_access_chains.h create mode 100644 source/fuzz/transformation_access_chain.cpp create mode 100644 source/fuzz/transformation_access_chain.h create mode 100644 test/fuzz/transformation_access_chain_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 330bbf04f7..4d5feea574 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -37,6 +37,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer.h fuzzer_context.h fuzzer_pass.h + fuzzer_pass_add_access_chains.h fuzzer_pass_add_composite_types.h fuzzer_pass_add_dead_blocks.h fuzzer_pass_add_dead_breaks.h @@ -71,6 +72,7 @@ if(SPIRV_BUILD_FUZZER) replayer.h shrinker.h transformation.h + transformation_access_chain.h transformation_add_constant_boolean.h transformation_add_constant_composite.h transformation_add_constant_scalar.h @@ -119,6 +121,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer.cpp fuzzer_context.cpp fuzzer_pass.cpp + fuzzer_pass_add_access_chains.cpp fuzzer_pass_add_composite_types.cpp fuzzer_pass_add_dead_blocks.cpp fuzzer_pass_add_dead_breaks.cpp @@ -152,6 +155,7 @@ if(SPIRV_BUILD_FUZZER) replayer.cpp shrinker.cpp transformation.cpp + transformation_access_chain.cpp transformation_add_constant_boolean.cpp transformation_add_constant_composite.cpp transformation_add_constant_scalar.cpp diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index a427619642..6c2821c6e2 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -21,6 +21,7 @@ #include "fuzzer_pass_adjust_memory_operands_masks.h" #include "source/fuzz/fact_manager.h" #include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass_add_access_chains.h" #include "source/fuzz/fuzzer_pass_add_composite_types.h" #include "source/fuzz/fuzzer_pass_add_dead_blocks.h" #include "source/fuzz/fuzzer_pass_add_dead_breaks.h" @@ -184,6 +185,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( // Apply some semantics-preserving passes. std::vector> passes; while (passes.empty()) { + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index a43466138a..12657720c3 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -23,6 +23,7 @@ namespace { // Default pairs of probabilities for applying various // transformations. All values are percentages. Keep them in alphabetical order. +const std::pair kChanceOfAddingAccessChain = {5, 50}; const std::pair kChanceOfAddingAnotherStructField = {20, 90}; const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; @@ -50,6 +51,8 @@ const std::pair kChanceOfChoosingStructTypeVsArrayType = { const std::pair kChanceOfConstructingComposite = {20, 50}; const std::pair kChanceOfCopyingObject = {20, 50}; const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; +const std::pair kChanceOfGoingDeeperWhenMakingAccessChain = + {50, 95}; const std::pair kChanceOfMakingDonorLivesafe = {40, 60}; const std::pair kChanceOfMergingBlocks = {20, 95}; const std::pair kChanceOfMovingBlockDown = {20, 50}; @@ -81,8 +84,14 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id) : random_generator_(random_generator), next_fresh_id_(min_fresh_id), + max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount), + max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount), + max_loop_limit_(kDefaultMaxLoopLimit), + max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit), go_deeper_in_constant_obfuscation_( kDefaultGoDeeperInConstantObfuscation) { + chance_of_adding_access_chain_ = + ChooseBetweenMinAndMax(kChanceOfAddingAccessChain); chance_of_adding_another_struct_field_ = ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); chance_of_adding_array_or_struct_type_ = @@ -122,6 +131,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); chance_of_donating_additional_module_ = ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule); + chance_of_going_deeper_when_making_access_chain_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain); chance_of_making_donor_livesafe_ = ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe); chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks); @@ -134,10 +145,6 @@ 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; - max_loop_limit_ = kDefaultMaxLoopLimit; - max_new_array_size_limit_ = kDefaultMaxNewArraySizeLimit; } FuzzerContext::~FuzzerContext() = default; diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 1d1245cebe..23127ff97b 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -68,6 +68,9 @@ class FuzzerContext { // Probabilities associated with applying various transformations. // Keep them in alphabetical order. + uint32_t GetChanceOfAddingAccessChain() { + return chance_of_adding_access_chain_; + } uint32_t GetChanceOfAddingAnotherStructField() { return chance_of_adding_another_struct_field_; } @@ -119,6 +122,9 @@ class FuzzerContext { uint32_t GetChanceOfDonatingAdditionalModule() { return chance_of_donating_additional_module_; } + uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() { + return chance_of_going_deeper_when_making_access_chain_; + } uint32_t ChanceOfMakingDonorLivesafe() { return chance_of_making_donor_livesafe_; } @@ -148,8 +154,11 @@ class FuzzerContext { return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; } - // Functions to control how deeply to recurse. - // Keep them in alphabetical order. + // Other functions to control transformations. Keep them in alphabetical + // order. + uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) { + return random_generator_->RandomUint32(composite_size_bound); + } bool GoDeeperInConstantObfuscation(uint32_t depth) { return go_deeper_in_constant_obfuscation_(depth, random_generator_); } @@ -162,6 +171,7 @@ class FuzzerContext { // Probabilities associated with applying various transformations. // Keep them in alphabetical order. + uint32_t chance_of_adding_access_chain_; uint32_t chance_of_adding_another_struct_field_; uint32_t chance_of_adding_array_or_struct_type_; uint32_t chance_of_adding_dead_block_; @@ -183,6 +193,7 @@ class FuzzerContext { uint32_t chance_of_constructing_composite_; uint32_t chance_of_copying_object_; uint32_t chance_of_donating_additional_module_; + uint32_t chance_of_going_deeper_when_making_access_chain_; uint32_t chance_of_making_donor_livesafe_; uint32_t chance_of_merging_blocks_; uint32_t chance_of_moving_block_down_; diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 1ecfa8d341..4a22a211ce 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -219,21 +219,27 @@ uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count, return result; } -uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType( - bool is_signed, SpvStorageClass storage_class) { - auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed); - opt::analysis::Pointer pointer_type( - GetIRContext()->get_type_mgr()->GetType(uint32_type_id), storage_class); - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&pointer_type); +uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id, + SpvStorageClass storage_class) { + // We do not use the type manager here, due to problems related to isomorphic + // but distinct structs not being regarded as different. + auto existing_id = fuzzerutil::MaybeGetPointerType( + GetIRContext(), base_type_id, storage_class); if (existing_id) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); ApplyTransformation( - TransformationAddTypePointer(result, storage_class, uint32_type_id)); + TransformationAddTypePointer(result, storage_class, base_type_id)); return result; } +uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType( + bool is_signed, SpvStorageClass storage_class) { + return FindOrCreatePointerType(FindOrCreate32BitIntegerType(is_signed), + storage_class); +} + uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed) { auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed); diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 68531796ff..7052685e9f 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -125,6 +125,12 @@ class FuzzerPass { // type itself do not exist, transformations are applied to add them. uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count); + // Returns the id of a pointer type with base type |base_type_id| (which must + // already exist) and storage class |storage_class|. A transformation is + // applied to add the pointer if it does not already exist. + uint32_t FindOrCreatePointerType(uint32_t base_type_id, + SpvStorageClass storage_class); + // Returns the id of an OpTypePointer instruction, with a 32-bit integer base // type of signedness specified by |is_signed|. If the pointer type or // required integer base type do not exist, transformations are applied to add diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp new file mode 100644 index 0000000000..11f368e60e --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_access_chains.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_access_chain.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default; + +void FuzzerPassAddAccessChains::Apply() { + MaybeAddTransformationBeforeEachInstruction( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(inst_it->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Check whether it is legitimate to insert an access chain + // instruction before this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAccessChain, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting a load here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAccessChain())) { + return; + } + + // Get all of the pointers that are currently in scope, excluding + // explicitly null and undefined pointers. + std::vector relevant_pointer_instructions = + FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + // A pointer needs both a result and type id. + return false; + } + switch (instruction->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + // Do not allow making an access chain from a null or + // undefined pointer. (We can eliminate these cases + // before actually checking that the instruction is a + // pointer.) + return false; + default: + break; + } + // If the instruction has pointer type, we can legitimately + // make an access chain from it. + return context->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == SpvOpTypePointer; + }); + + // At this point, |relevant_instructions| contains all the pointers + // we might think of making an access chain from. + if (relevant_pointer_instructions.empty()) { + return; + } + + auto chosen_pointer = + relevant_pointer_instructions[GetFuzzerContext()->RandomIndex( + relevant_pointer_instructions)]; + std::vector index_ids; + auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef( + chosen_pointer->type_id()); + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + while (true) { + auto subobject_type = + GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id); + if (!spvOpcodeIsComposite(subobject_type->opcode())) { + break; + } + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperWhenMakingAccessChain())) { + break; + } + uint32_t bound; + switch (subobject_type->opcode()) { + case SpvOpTypeArray: + bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext()); + break; + case SpvOpTypeMatrix: + case SpvOpTypeVector: + bound = subobject_type->GetSingleWordInOperand(1); + break; + case SpvOpTypeStruct: + bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type); + break; + default: + assert(false && "Not a composite type opcode."); + // Set the bound to a value in order to keep release compilers + // happy. + bound = 0; + break; + } + if (bound == 0) { + // It is possible for a composite type to legitimately have zero + // sub-components, at least in the case of a struct, which + // can have no fields. + break; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We + // could allow non-constant indices when looking up non-structs, + // using clamping to ensure they are in-bounds. + uint32_t index_value = + GetFuzzerContext()->GetRandomIndexForAccessChain(bound); + index_ids.push_back(FindOrCreate32BitIntegerConstant( + index_value, GetFuzzerContext()->ChooseEven())); + switch (subobject_type->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + subobject_type_id = subobject_type->GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: + subobject_type_id = + subobject_type->GetSingleWordInOperand(index_value); + break; + default: + assert(false && "Not a composite type opcode."); + } + } + // The transformation we are about to create will only apply if a + // pointer suitable for the access chain's result type exists, so we + // create one if it does not. + FindOrCreatePointerType(subobject_type_id, + static_cast( + pointer_type->GetSingleWordInOperand(0))); + // Apply the transformation to add an access chain. + ApplyTransformation(TransformationAccessChain( + GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(), + index_ids, instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h new file mode 100644 index 0000000000..7e8ed6129f --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_access_chains.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds access chains based on pointers available in +// the module. Other passes can use these access chains, e.g. by loading from +// them. +class FuzzerPassAddAccessChains : public FuzzerPass { + public: + FuzzerPassAddAccessChains(opt::IRContext* ir_context, + FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddAccessChains(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index cb90143f70..26961c8d7e 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -262,44 +262,48 @@ std::vector RepeatedFieldToVector( return result; } +uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context, + uint32_t base_object_type_id, + uint32_t index) { + auto should_be_composite_type = + context->get_def_use_mgr()->GetDef(base_object_type_id); + assert(should_be_composite_type && "The type should exist."); + switch (should_be_composite_type->opcode()) { + case SpvOpTypeArray: { + auto array_length = GetArraySize(*should_be_composite_type, context); + if (array_length == 0 || index >= array_length) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(0); + } + case SpvOpTypeMatrix: + case SpvOpTypeVector: { + auto count = should_be_composite_type->GetSingleWordInOperand(1); + if (index >= count) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(0); + } + case SpvOpTypeStruct: { + if (index >= GetNumberOfStructMembers(*should_be_composite_type)) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(index); + } + default: + return 0; + } +} + uint32_t WalkCompositeTypeIndices( opt::IRContext* context, uint32_t base_object_type_id, const google::protobuf::RepeatedField& indices) { uint32_t sub_object_type_id = base_object_type_id; for (auto index : indices) { - auto should_be_composite_type = - context->get_def_use_mgr()->GetDef(sub_object_type_id); - assert(should_be_composite_type && "The type should exist."); - switch (should_be_composite_type->opcode()) { - case SpvOpTypeArray: { - auto array_length = GetArraySize(*should_be_composite_type, context); - if (array_length == 0 || index >= array_length) { - return 0; - } - sub_object_type_id = - should_be_composite_type->GetSingleWordInOperand(0); - break; - } - case SpvOpTypeMatrix: - case SpvOpTypeVector: { - auto count = should_be_composite_type->GetSingleWordInOperand(1); - if (index >= count) { - return 0; - } - sub_object_type_id = - should_be_composite_type->GetSingleWordInOperand(0); - break; - } - case SpvOpTypeStruct: { - if (index >= GetNumberOfStructMembers(*should_be_composite_type)) { - return 0; - } - sub_object_type_id = - should_be_composite_type->GetSingleWordInOperand(index); - break; - } - default: - return 0; + sub_object_type_id = + WalkOneCompositeTypeIndex(context, sub_object_type_id, index); + if (!sub_object_type_id) { + return 0; } } return sub_object_type_id; @@ -501,6 +505,23 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, context->get_def_use_mgr()->GetDef(pointer_type_id)); } +uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, + SpvStorageClass storage_class) { + for (auto& inst : context->types_values()) { + switch (inst.opcode()) { + case SpvOpTypePointer: + if (inst.GetSingleWordInOperand(0) == storage_class && + inst.GetSingleWordInOperand(1) == pointee_type_id) { + return inst.result_id(); + } + break; + default: + break; + } + } + return 0; +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 292cd9f969..daa836c1d4 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -98,6 +98,21 @@ bool IsCompositeType(const opt::analysis::Type* type); std::vector RepeatedFieldToVector( const google::protobuf::RepeatedField& repeated_field); +// Given a type id, |base_object_type_id|, returns 0 if the type is not a +// composite type or if |index| is too large to be used as an index into the +// composite. Otherwise returns the type id of the type associated with the +// composite's index. +// +// Example: if |base_object_type_id| is 10, and we have: +// +// %10 = OpTypeStruct %3 %4 %5 +// +// then 3 will be returned if |index| is 0, 5 if |index| is 2, and 0 if index +// is 3 or larger. +uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context, + uint32_t base_object_type_id, + uint32_t index); + // Given a type id, |base_object_type_id|, checks that the given sequence of // |indices| is suitable for indexing into this type. Returns the id of the // type of the final sub-object reached via the indices if they are valid, and @@ -181,6 +196,11 @@ SpvStorageClass GetStorageClassFromPointerType( SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, uint32_t pointer_type_id); +// Returns the id of a pointer with pointee type |pointee_type_id| and storage +// class |storage_class|, if it exists, and 0 otherwise. +uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, + SpvStorageClass storage_class); + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 8a931ba083..9773b60674 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -341,12 +341,32 @@ message Transformation { TransformationLoad load = 36; TransformationStore store = 37; TransformationFunctionCall function_call = 38; + TransformationAccessChain access_chain = 39; // Add additional option using the next available number. } } // Keep transformation message types in alphabetical order: +message TransformationAccessChain { + + // Adds an access chain instruction based on a given pointer and indices. + + // Result id for the access chain + uint32 fresh_id = 1; + + // The pointer from which the access chain starts + uint32 pointer_id = 2; + + // Zero or more access chain indices + repeated uint32 index_id = 3; + + // A descriptor for an instruction in a block before which the new + // OpAccessChain instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + +} + message TransformationAddConstantBoolean { // Supports adding the constants true and false to a module, which may be diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index d41a7345ca..52fcfd73da 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -17,6 +17,7 @@ #include #include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_access_chain.h" #include "source/fuzz/transformation_add_constant_boolean.h" #include "source/fuzz/transformation_add_constant_composite.h" #include "source/fuzz/transformation_add_constant_scalar.h" @@ -65,6 +66,8 @@ Transformation::~Transformation() = default; std::unique_ptr Transformation::FromMessage( const protobufs::Transformation& message) { switch (message.transformation_case()) { + case protobufs::Transformation::TransformationCase::kAccessChain: + return MakeUnique(message.access_chain()); case protobufs::Transformation::TransformationCase::kAddConstantBoolean: return MakeUnique( message.add_constant_boolean()); diff --git a/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp new file mode 100644 index 0000000000..8c3100640e --- /dev/null +++ b/source/fuzz/transformation_access_chain.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_access_chain.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAccessChain::TransformationAccessChain( + const spvtools::fuzz::protobufs::TransformationAccessChain& message) + : message_(message) {} + +TransformationAccessChain::TransformationAccessChain( + uint32_t fresh_id, uint32_t pointer_id, + const std::vector& index_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_pointer_id(pointer_id); + for (auto id : index_id) { + message_.add_index_id(id); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationAccessChain::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // The result id must be fresh + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + // The pointer id must exist and have a type. + auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + // The type must indeed be a pointer + auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id()); + if (pointer_type->opcode() != SpvOpTypePointer) { + return false; + } + + // The described instruction to insert before must exist and be a suitable + // point where an OpAccessChain instruction could be inserted. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), context); + if (!instruction_to_insert_before) { + return false; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAccessChain, instruction_to_insert_before)) { + return false; + } + + // Do not allow making an access chain from a null or undefined pointer, as + // we do not want to allow accessing such pointers. This might be acceptable + // in dead blocks, but we conservatively avoid it. + switch (pointer->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3185): When + // fuzzing for real we would like an 'assert(false)' here. But we also + // want to be able to write negative unit tests. + return false; + default: + break; + } + + // The pointer on which the access chain is to be based needs to be available + // (according to dominance rules) at the insertion point. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + context, instruction_to_insert_before, message_.pointer_id())) { + return false; + } + + // We now need to use the given indices to walk the type structure of the + // base type of the pointer, making sure that (a) the indices correspond to + // integers, and (b) these integer values are in-bounds. + + // Start from the base type of the pointer. + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + + // Consider the given index ids in turn. + for (auto index_id : message_.index_id()) { + // Try to get the integer value associated with this index is. The first + // component of the result will be false if the id did not correspond to an + // integer. Otherwise, the integer with which the id is associated is the + // second component. + std::pair maybe_index_value = + GetIndexValue(context, index_id); + if (!maybe_index_value.first) { + // There was no integer: this index is no good. + return false; + } + // Try to walk down the type using this index. This will yield 0 if the + // type is not a composite or the index is out of bounds, and the id of + // the next type otherwise. + subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + context, subobject_type_id, maybe_index_value.second); + if (!subobject_type_id) { + // Either the type was not a composite (so that too many indices were + // provided), or the index was out of bounds. + return false; + } + } + // At this point, |subobject_type_id| is the type of the value targeted by + // the new access chain. The result type of the access chain should be a + // pointer to this type, with the same storage class as for the original + // pointer. Such a pointer type needs to exist in the module. + // + // We do not use the type manager to look up this type, due to problems + // associated with pointers to isomorphic structs being regarded as the same. + return fuzzerutil::MaybeGetPointerType( + context, subobject_type_id, + static_cast( + pointer_type->GetSingleWordInOperand(0))) != 0; +} + +void TransformationAccessChain::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + // The operands to the access chain are the pointer followed by the indices. + // The result type of the access chain is determined by where the indices + // lead. We thus push the pointer to a sequence of operands, and then follow + // the indices, pushing each to the operand list and tracking the type + // obtained by following it. Ultimately this yields the type of the + // component reached by following all the indices, and the result type is + // a pointer to this component type. + opt::Instruction::OperandList operands; + + // Add the pointer id itself. + operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}); + + // Start walking the indices, starting with the pointer's base type. + auto pointer_type = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id()); + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + + // Go through the index ids in turn. + for (auto index_id : message_.index_id()) { + // Add the index id to the operands. + operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}}); + // Get the integer value associated with the index id. + uint32_t index_value = GetIndexValue(context, index_id).second; + // Walk to the next type in the composite object using this index. + subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + context, subobject_type_id, index_value); + } + // The access chain's result type is a pointer to the composite component that + // was reached after following all indices. The storage class is that of the + // original pointer. + uint32_t result_type = fuzzerutil::MaybeGetPointerType( + context, subobject_type_id, + static_cast(pointer_type->GetSingleWordInOperand(0))); + + // Add the access chain instruction to the module, and update the module's id + // bound. + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + FindInstruction(message_.instruction_to_insert_before(), context) + ->InsertBefore( + MakeUnique(context, SpvOpAccessChain, result_type, + message_.fresh_id(), operands)); + + // Conservatively invalidate all analyses. + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // If the base pointer's pointee value was irrelevant, the same is true of the + // pointee value of the result of this access chain. + if (fact_manager->PointeeValueIsIrrelevant(message_.pointer_id())) { + fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAccessChain::ToMessage() const { + protobufs::Transformation result; + *result.mutable_access_chain() = message_; + return result; +} + +std::pair TransformationAccessChain::GetIndexValue( + opt::IRContext* context, uint32_t index_id) const { + auto index_instruction = context->get_def_use_mgr()->GetDef(index_id); + if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could + // allow non-constant indices when looking up non-structs, using clamping + // to ensure they are in-bounds. + return {false, 0}; + } + auto index_type = + context->get_def_use_mgr()->GetDef(index_instruction->type_id()); + if (index_type->opcode() != SpvOpTypeInt || + index_type->GetSingleWordInOperand(0) != 32) { + return {false, 0}; + } + return {true, index_instruction->GetSingleWordInOperand(0)}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_access_chain.h b/source/fuzz/transformation_access_chain.h new file mode 100644 index 0000000000..92d9e6a6e6 --- /dev/null +++ b/source/fuzz/transformation_access_chain.h @@ -0,0 +1,80 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ + +#include + +#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 TransformationAccessChain : public Transformation { + public: + explicit TransformationAccessChain( + const protobufs::TransformationAccessChain& message); + + TransformationAccessChain( + uint32_t fresh_id, uint32_t pointer_id, + const std::vector& index_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is legitimate to insert an OpAccessChain instruction + // - |message_.pointer_id| must be a result id with pointer type that is + // available (according to dominance rules) at the insertion point. + // - The pointer must not be OpConstantNull or OpUndef + // - |message_.index_id| must be a sequence of ids of 32-bit integer constants + // such that it is possible to walk the pointee type of + // |message_.pointer_id| using these indices, remaining in-bounds. + // - If type t is the final type reached by walking these indices, the module + // must include an instruction "OpTypePointer SC %t" where SC is the storage + // class associated with |message_.pointer_id| + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an instruction of the form: + // |message_.fresh_id| = OpAccessChain %ptr |message_.index_id| + // where %ptr is the result if of an instruction declaring a pointer to the + // type reached by walking the pointee type of |message_.pointer_id| using + // the indices in |message_.index_id|, and with the same storage class as + // |message_.pointer_id|. + // + // If |fact_manager| reports that |message_.pointer_id| has an irrelevant + // pointee value, then the fact that |message_.fresh_id| (the result of the + // access chain) also has an irrelevant pointee value is also recorded. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns {false, 0} if |index_id| does not correspond to a 32-bit integer + // constant. Otherwise, returns {true, value}, where value is the value of + // the 32-bit integer constant to which |index_id| corresponds. + std::pair GetIndexValue(opt::IRContext* context, + uint32_t index_id) const; + + protobufs::TransformationAccessChain message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 4a423a9a1c..4211ff2bcb 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -24,6 +24,7 @@ if (${SPIRV_BUILD_FUZZER}) fuzzer_pass_add_useful_constructs_test.cpp fuzzer_pass_donate_modules_test.cpp instruction_descriptor_test.cpp + transformation_access_chain_test.cpp transformation_add_constant_boolean_test.cpp transformation_add_constant_composite_test.cpp transformation_add_constant_scalar_test.cpp diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp new file mode 100644 index 0000000000..516d371b49 --- /dev/null +++ b/test/fuzz/transformation_access_chain_test.cpp @@ -0,0 +1,448 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_access_chain.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAccessChainTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %50 = OpTypeMatrix %7 2 + %70 = OpTypePointer Function %7 + %71 = OpTypePointer Function %50 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpTypeFunction %10 %9 %11 + %17 = OpConstant %10 0 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %20 = OpTypePointer Function %6 + %99 = OpTypePointer Private %6 + %29 = OpConstant %6 0 + %30 = OpConstant %6 1 + %31 = OpConstantComposite %7 %29 %30 + %32 = OpConstant %6 2 + %33 = OpConstantComposite %8 %31 %32 + %35 = OpConstant %10 10 + %51 = OpConstant %18 10 + %80 = OpConstant %18 0 + %81 = OpConstant %10 1 + %82 = OpConstant %18 2 + %83 = OpConstant %10 3 + %84 = OpConstant %18 4 + %85 = OpConstant %10 5 + %52 = OpTypeArray %50 %51 + %53 = OpTypePointer Private %52 + %45 = OpUndef %9 + %46 = OpConstantNull %9 + %47 = OpTypePointer Private %8 + %48 = OpVariable %47 Private + %54 = OpVariable %53 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %28 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %9 Function + %38 = OpVariable %11 Function + %44 = OpCopyObject %9 %36 + OpStore %28 %33 + OpStore %34 %35 + %37 = OpLoad %8 %28 + OpStore %36 %37 + %39 = OpLoad %10 %34 + OpStore %38 %39 + %40 = OpFunctionCall %10 %15 %36 %38 + %41 = OpLoad %10 %34 + %42 = OpIAdd %10 %41 %40 + OpStore %34 %42 + OpReturn + OpFunctionEnd + %15 = OpFunction %10 None %12 + %13 = OpFunctionParameter %9 + %14 = OpFunctionParameter %11 + %16 = OpLabel + %21 = OpAccessChain %20 %13 %17 %19 + %43 = OpCopyObject %9 %13 + %22 = OpLoad %6 %21 + %23 = OpConvertFToS %10 %22 + %24 = OpLoad %10 %14 + %25 = OpIAdd %10 %23 %24 + OpReturnValue %25 + 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())); + + // Types: + // Ptr | Pointee | Storage class | GLSL for pointee | Ids of this type + // ----+---------+---------------+---------------------+------------------ + // 9 | 8 | Function | struct(vec2, float) | 28, 36, 44, 13, 43 + // 11 | 10 | Function | int | 34, 38, 14 + // 20 | 6 | Function | float | - + // 99 | 6 | Private | float | - + // 53 | 52 | Private | mat2x2[10] | 54 + // 47 | 8 | Private | struct(vec2, float) | 48 + // 70 | 7 | Function | vec2 | - + // 71 | 59 | Function | mat2x2 | - + + // Indices 0-5 are in ids 80-85 + + FactManager fact_manager; + fact_manager.AddFactValueOfPointeeIsIrrelevant(54); + + // Bad: id is not fresh + ASSERT_FALSE(TransformationAccessChain( + 43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id does not exist + ASSERT_FALSE(TransformationAccessChain( + 100, 1000, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id is not a type + ASSERT_FALSE(TransformationAccessChain( + 100, 5, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer id is not a pointer + ASSERT_FALSE(TransformationAccessChain( + 100, 23, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: index id does not exist + ASSERT_FALSE(TransformationAccessChain( + 100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: index id is not a constant + ASSERT_FALSE(TransformationAccessChain( + 100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: too many indices + ASSERT_FALSE( + TransformationAccessChain(100, 43, {80, 80, 80}, + MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: index id is out of bounds + ASSERT_FALSE( + TransformationAccessChain(100, 43, {80, 83}, + MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: attempt to insert before variable + ASSERT_FALSE(TransformationAccessChain( + 100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer not available + ASSERT_FALSE( + TransformationAccessChain( + 100, 43, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: instruction descriptor does not identify anything + ASSERT_FALSE(TransformationAccessChain( + 100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer is null + ASSERT_FALSE(TransformationAccessChain( + 100, 45, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer is undef + ASSERT_FALSE(TransformationAccessChain( + 100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: pointer to result type does not exist + ASSERT_FALSE(TransformationAccessChain( + 100, 52, {0}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + { + TransformationAccessChain transformation( + 100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100)); + } + + { + TransformationAccessChain transformation( + 101, 28, {81}, MakeInstructionDescriptor(42, SpvOpReturn, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); + } + + { + TransformationAccessChain transformation( + 102, 36, {80, 81}, MakeInstructionDescriptor(37, SpvOpStore, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102)); + } + + { + TransformationAccessChain transformation( + 103, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103)); + } + + { + TransformationAccessChain transformation( + 104, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104)); + } + + { + TransformationAccessChain transformation( + 105, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105)); + } + + { + TransformationAccessChain transformation( + 106, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(106)); + } + + { + TransformationAccessChain transformation( + 107, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(107)); + } + + { + TransformationAccessChain transformation( + 108, 54, {85, 81, 81}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(108)); + } + + { + TransformationAccessChain transformation( + 109, 48, {80, 80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(109)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %50 = OpTypeMatrix %7 2 + %70 = OpTypePointer Function %7 + %71 = OpTypePointer Function %50 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpTypeFunction %10 %9 %11 + %17 = OpConstant %10 0 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %20 = OpTypePointer Function %6 + %99 = OpTypePointer Private %6 + %29 = OpConstant %6 0 + %30 = OpConstant %6 1 + %31 = OpConstantComposite %7 %29 %30 + %32 = OpConstant %6 2 + %33 = OpConstantComposite %8 %31 %32 + %35 = OpConstant %10 10 + %51 = OpConstant %18 10 + %80 = OpConstant %18 0 + %81 = OpConstant %10 1 + %82 = OpConstant %18 2 + %83 = OpConstant %10 3 + %84 = OpConstant %18 4 + %85 = OpConstant %10 5 + %52 = OpTypeArray %50 %51 + %53 = OpTypePointer Private %52 + %45 = OpUndef %9 + %46 = OpConstantNull %9 + %47 = OpTypePointer Private %8 + %48 = OpVariable %47 Private + %54 = OpVariable %53 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %28 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %9 Function + %38 = OpVariable %11 Function + %44 = OpCopyObject %9 %36 + %103 = OpAccessChain %9 %44 + OpStore %28 %33 + %105 = OpAccessChain %11 %34 + OpStore %34 %35 + %37 = OpLoad %8 %28 + %102 = OpAccessChain %20 %36 %80 %81 + OpStore %36 %37 + %39 = OpLoad %10 %34 + OpStore %38 %39 + %106 = OpAccessChain %11 %38 + %40 = OpFunctionCall %10 %15 %36 %38 + %41 = OpLoad %10 %34 + %42 = OpIAdd %10 %41 %40 + OpStore %34 %42 + %101 = OpAccessChain %20 %28 %81 + OpReturn + OpFunctionEnd + %15 = OpFunction %10 None %12 + %13 = OpFunctionParameter %9 + %14 = OpFunctionParameter %11 + %16 = OpLabel + %104 = OpAccessChain %70 %13 %80 + %21 = OpAccessChain %20 %13 %17 %19 + %43 = OpCopyObject %9 %13 + %22 = OpLoad %6 %21 + %23 = OpConvertFToS %10 %22 + %100 = OpAccessChain %70 %43 %80 + %107 = OpAccessChain %11 %14 + %108 = OpAccessChain %99 %54 %85 %81 %81 + %109 = OpAccessChain %99 %48 %80 %80 + %24 = OpLoad %10 %14 + %25 = OpIAdd %10 %23 %24 + OpReturnValue %25 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAccessChainTest, IsomorphicStructs) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %11 %12 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Private %7 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Private %9 + %11 = OpVariable %8 Private + %12 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = 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; + + { + TransformationAccessChain transformation( + 100, 11, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0)); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + { + TransformationAccessChain transformation( + 101, 12, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0)); + 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" %11 %12 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Private %7 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Private %9 + %11 = OpVariable %8 Private + %12 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpAccessChain %8 %11 + %101 = OpAccessChain %10 %12 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools From 77fefe765c060afe664af01fb9801166081cb194 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Fri, 14 Feb 2020 10:04:03 +0000 Subject: [PATCH 21/88] spirvfuzz: Fix type-related bug, change undef to zero, and add assert (#3188) This fixes a bug where the type id of a type instruction, rather than its result id, was being used. It also favours using zero as the return value when replacing an OpKill or OpUnreachable with a return instruction, and adds a check that the donor module is valid when doing module donation. Fixes #3187. --- source/fuzz/fuzzer_pass_donate_modules.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 3368665852..27d8a6e5ea 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -62,6 +62,8 @@ void FuzzerPassDonateModules::Apply() { std::unique_ptr donor_ir_context = donor_suppliers_.at( GetFuzzerContext()->RandomIndex(donor_suppliers_))(); assert(donor_ir_context != nullptr && "Supplying of donor failed"); + assert(fuzzerutil::IsValid(donor_ir_context.get()) && + "The donor module must be valid"); // Donate the supplied module. // // Randomly decide whether to make the module livesafe (see @@ -668,9 +670,12 @@ void FuzzerPassDonateModules::HandleFunctions( // The return type is void, so we don't need a return value. kill_unreachable_return_value_id = 0; } else { - // We do need a return value; we use OpUndef. + // We do need a return value; we use zero. + assert(function_return_type_inst->opcode() != SpvOpTypePointer && + "Function return type must not be a pointer."); kill_unreachable_return_value_id = - FindOrCreateGlobalUndef(function_return_type_inst->type_id()); + FindOrCreateZeroConstant(original_id_to_donated_id->at( + function_return_type_inst->result_id())); } // Add the function in a livesafe manner. ApplyTransformation(TransformationAddFunction( From 4d912f4e6022d4d77ea95b2c425da79773a6574e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Iglesias=20Gons=C3=A1lvez?= Date: Tue, 18 Feb 2020 14:23:41 +0100 Subject: [PATCH 22/88] spirv-val: Add support for SPV_AMD_shader_image_load_store_lod (#3186) According to SPV_AMD_shader_image_load_store_lod spec, Lod operand is valid with OpImageRead, OpImageWrite, or OpImageSparseRead if the extension is enabled. --- source/val/validate_image.cpp | 14 ++- test/val/val_image_test.cpp | 163 ++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index bc2753cf9e..ed960d1639 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -149,6 +149,17 @@ bool IsExplicitLod(SpvOp opcode) { return false; } +bool IsValidLodOperand(const ValidationState_t& _, SpvOp opcode) { + switch (opcode) { + case SpvOpImageRead: + case SpvOpImageWrite: + case SpvOpImageSparseRead: + return _.HasCapability(SpvCapabilityImageReadWriteLodAMD); + default: + return IsExplicitLod(opcode); + } +} + // Returns true if the opcode is a Image instruction which applies // homogenous projection to the coordinates. bool IsProj(SpvOp opcode) { @@ -248,6 +259,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, const bool is_implicit_lod = IsImplicitLod(opcode); const bool is_explicit_lod = IsExplicitLod(opcode); + const bool is_valid_lod_operand = IsValidLodOperand(_, opcode); // The checks should be done in the order of definition of OperandImage. @@ -277,7 +289,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, } if (mask & SpvImageOperandsLodMask) { - if (!is_explicit_lod && opcode != SpvOpImageFetch && + if (!is_valid_lod_operand && opcode != SpvOpImageFetch && opcode != SpvOpImageSparseFetch) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image Operand Lod can only be used with ExplicitLod opcodes " diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 433c9faed8..570dd16697 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -4841,6 +4841,169 @@ TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14Good) { EXPECT_THAT(getDiagnosticString(), Eq("")); } +TEST_F(ValidateImage, ReadLodAMDSuccess1) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability StorageImageReadWithoutFormat\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, ReadLodAMDSuccess2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability Image1D\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, ReadLodAMDSuccess3) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec3_012 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability ImageCubeArray\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, ReadLodAMDNeedCapability) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec3_012 Lod %u32_0 +)"; + + const std::string extra = "\nOpCapability ImageCubeArray\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod can only be used with ExplicitLod " + "opcodes and OpImageFetch")); +} + +TEST_F(ValidateImage, WriteLodAMDSuccess1) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability StorageImageWriteWithoutFormat\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, WriteLodAMDSuccess2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +OpImageWrite %img %u32_1 %f32vec4_0000 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability Image1D\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, WriteLodAMDSuccess3) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +OpImageWrite %img %u32vec3_012 %f32vec4_0000 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability ImageCubeArray\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, WriteLodAMDNeedCapability) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +OpImageWrite %img %u32vec3_012 %f32vec4_0000 Lod %u32_0 +)"; + + const std::string extra = "\nOpCapability ImageCubeArray\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod can only be used with ExplicitLod " + "opcodes and OpImageFetch")); +} + +TEST_F(ValidateImage, SparseReadLodAMDSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability StorageImageReadWithoutFormat\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, SparseReadLodAMDNeedCapability) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod can only be used with ExplicitLod " + "opcodes and OpImageFetch")); +} + // No negative tests for ZeroExtend since we don't truly know the // texel format. From 79f8caf9154a0328a87424354bd10ab69e811185 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 18 Feb 2020 14:01:56 +0000 Subject: [PATCH 23/88] vscode: Add missing fields to schema.Opcode (#3169) These were declared in the `Opcode` struct type, but were never assigned. They're not actually used by the language server, but I reused this go schema package for a local experiment and found they were missing. --- utils/vscode/src/schema/schema.go | 1050 ++++++++++++++++++++++++ utils/vscode/src/schema/schema.go.tmpl | 2 + 2 files changed, 1052 insertions(+) diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go index 9f87f50c22..66bd7dc111 100755 --- a/utils/vscode/src/schema/schema.go +++ b/utils/vscode/src/schema/schema.go @@ -920,11 +920,15 @@ var ( OpNop = &Opcode { Opname: "OpNop", + Class: "Miscellaneous", + Opcode: 0, Operands: []Operand { }, } OpUndef = &Opcode { Opname: "OpUndef", + Class: "Miscellaneous", + Opcode: 1, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -940,6 +944,8 @@ var ( } OpSourceContinued = &Opcode { Opname: "OpSourceContinued", + Class: "Debug", + Opcode: 2, Operands: []Operand { Operand { Kind: OperandKindLiteralString, @@ -950,6 +956,8 @@ var ( } OpSource = &Opcode { Opname: "OpSource", + Class: "Debug", + Opcode: 3, Operands: []Operand { Operand { Kind: OperandKindSourceLanguage, @@ -975,6 +983,8 @@ var ( } OpSourceExtension = &Opcode { Opname: "OpSourceExtension", + Class: "Debug", + Opcode: 4, Operands: []Operand { Operand { Kind: OperandKindLiteralString, @@ -985,6 +995,8 @@ var ( } OpName = &Opcode { Opname: "OpName", + Class: "Debug", + Opcode: 5, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1000,6 +1012,8 @@ var ( } OpMemberName = &Opcode { Opname: "OpMemberName", + Class: "Debug", + Opcode: 6, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1020,6 +1034,8 @@ var ( } OpString = &Opcode { Opname: "OpString", + Class: "Debug", + Opcode: 7, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1035,6 +1051,8 @@ var ( } OpLine = &Opcode { Opname: "OpLine", + Class: "Debug", + Opcode: 8, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1055,6 +1073,8 @@ var ( } OpExtension = &Opcode { Opname: "OpExtension", + Class: "Extension", + Opcode: 10, Operands: []Operand { Operand { Kind: OperandKindLiteralString, @@ -1065,6 +1085,8 @@ var ( } OpExtInstImport = &Opcode { Opname: "OpExtInstImport", + Class: "Extension", + Opcode: 11, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1080,6 +1102,8 @@ var ( } OpExtInst = &Opcode { Opname: "OpExtInst", + Class: "Extension", + Opcode: 12, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1110,6 +1134,8 @@ var ( } OpMemoryModel = &Opcode { Opname: "OpMemoryModel", + Class: "Mode-Setting", + Opcode: 14, Operands: []Operand { Operand { Kind: OperandKindAddressingModel, @@ -1125,6 +1151,8 @@ var ( } OpEntryPoint = &Opcode { Opname: "OpEntryPoint", + Class: "Mode-Setting", + Opcode: 15, Operands: []Operand { Operand { Kind: OperandKindExecutionModel, @@ -1150,6 +1178,8 @@ var ( } OpExecutionMode = &Opcode { Opname: "OpExecutionMode", + Class: "Mode-Setting", + Opcode: 16, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1165,6 +1195,8 @@ var ( } OpCapability = &Opcode { Opname: "OpCapability", + Class: "Mode-Setting", + Opcode: 17, Operands: []Operand { Operand { Kind: OperandKindCapability, @@ -1175,6 +1207,8 @@ var ( } OpTypeVoid = &Opcode { Opname: "OpTypeVoid", + Class: "Type-Declaration", + Opcode: 19, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1185,6 +1219,8 @@ var ( } OpTypeBool = &Opcode { Opname: "OpTypeBool", + Class: "Type-Declaration", + Opcode: 20, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1195,6 +1231,8 @@ var ( } OpTypeInt = &Opcode { Opname: "OpTypeInt", + Class: "Type-Declaration", + Opcode: 21, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1215,6 +1253,8 @@ var ( } OpTypeFloat = &Opcode { Opname: "OpTypeFloat", + Class: "Type-Declaration", + Opcode: 22, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1230,6 +1270,8 @@ var ( } OpTypeVector = &Opcode { Opname: "OpTypeVector", + Class: "Type-Declaration", + Opcode: 23, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1250,6 +1292,8 @@ var ( } OpTypeMatrix = &Opcode { Opname: "OpTypeMatrix", + Class: "Type-Declaration", + Opcode: 24, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1270,6 +1314,8 @@ var ( } OpTypeImage = &Opcode { Opname: "OpTypeImage", + Class: "Type-Declaration", + Opcode: 25, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1320,6 +1366,8 @@ var ( } OpTypeSampler = &Opcode { Opname: "OpTypeSampler", + Class: "Type-Declaration", + Opcode: 26, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1330,6 +1378,8 @@ var ( } OpTypeSampledImage = &Opcode { Opname: "OpTypeSampledImage", + Class: "Type-Declaration", + Opcode: 27, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1345,6 +1395,8 @@ var ( } OpTypeArray = &Opcode { Opname: "OpTypeArray", + Class: "Type-Declaration", + Opcode: 28, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1365,6 +1417,8 @@ var ( } OpTypeRuntimeArray = &Opcode { Opname: "OpTypeRuntimeArray", + Class: "Type-Declaration", + Opcode: 29, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1380,6 +1434,8 @@ var ( } OpTypeStruct = &Opcode { Opname: "OpTypeStruct", + Class: "Type-Declaration", + Opcode: 30, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1395,6 +1451,8 @@ var ( } OpTypeOpaque = &Opcode { Opname: "OpTypeOpaque", + Class: "Type-Declaration", + Opcode: 31, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1410,6 +1468,8 @@ var ( } OpTypePointer = &Opcode { Opname: "OpTypePointer", + Class: "Type-Declaration", + Opcode: 32, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1430,6 +1490,8 @@ var ( } OpTypeFunction = &Opcode { Opname: "OpTypeFunction", + Class: "Type-Declaration", + Opcode: 33, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1450,6 +1512,8 @@ var ( } OpTypeEvent = &Opcode { Opname: "OpTypeEvent", + Class: "Type-Declaration", + Opcode: 34, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1460,6 +1524,8 @@ var ( } OpTypeDeviceEvent = &Opcode { Opname: "OpTypeDeviceEvent", + Class: "Type-Declaration", + Opcode: 35, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1470,6 +1536,8 @@ var ( } OpTypeReserveId = &Opcode { Opname: "OpTypeReserveId", + Class: "Type-Declaration", + Opcode: 36, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1480,6 +1548,8 @@ var ( } OpTypeQueue = &Opcode { Opname: "OpTypeQueue", + Class: "Type-Declaration", + Opcode: 37, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1490,6 +1560,8 @@ var ( } OpTypePipe = &Opcode { Opname: "OpTypePipe", + Class: "Type-Declaration", + Opcode: 38, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -1505,6 +1577,8 @@ var ( } OpTypeForwardPointer = &Opcode { Opname: "OpTypeForwardPointer", + Class: "Type-Declaration", + Opcode: 39, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1520,6 +1594,8 @@ var ( } OpConstantTrue = &Opcode { Opname: "OpConstantTrue", + Class: "Constant-Creation", + Opcode: 41, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1535,6 +1611,8 @@ var ( } OpConstantFalse = &Opcode { Opname: "OpConstantFalse", + Class: "Constant-Creation", + Opcode: 42, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1550,6 +1628,8 @@ var ( } OpConstant = &Opcode { Opname: "OpConstant", + Class: "Constant-Creation", + Opcode: 43, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1570,6 +1650,8 @@ var ( } OpConstantComposite = &Opcode { Opname: "OpConstantComposite", + Class: "Constant-Creation", + Opcode: 44, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1590,6 +1672,8 @@ var ( } OpConstantSampler = &Opcode { Opname: "OpConstantSampler", + Class: "Constant-Creation", + Opcode: 45, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1620,6 +1704,8 @@ var ( } OpConstantNull = &Opcode { Opname: "OpConstantNull", + Class: "Constant-Creation", + Opcode: 46, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1635,6 +1721,8 @@ var ( } OpSpecConstantTrue = &Opcode { Opname: "OpSpecConstantTrue", + Class: "Constant-Creation", + Opcode: 48, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1650,6 +1738,8 @@ var ( } OpSpecConstantFalse = &Opcode { Opname: "OpSpecConstantFalse", + Class: "Constant-Creation", + Opcode: 49, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1665,6 +1755,8 @@ var ( } OpSpecConstant = &Opcode { Opname: "OpSpecConstant", + Class: "Constant-Creation", + Opcode: 50, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1685,6 +1777,8 @@ var ( } OpSpecConstantComposite = &Opcode { Opname: "OpSpecConstantComposite", + Class: "Constant-Creation", + Opcode: 51, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1705,6 +1799,8 @@ var ( } OpSpecConstantOp = &Opcode { Opname: "OpSpecConstantOp", + Class: "Constant-Creation", + Opcode: 52, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1725,6 +1821,8 @@ var ( } OpFunction = &Opcode { Opname: "OpFunction", + Class: "Function", + Opcode: 54, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1750,6 +1848,8 @@ var ( } OpFunctionParameter = &Opcode { Opname: "OpFunctionParameter", + Class: "Function", + Opcode: 55, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1765,11 +1865,15 @@ var ( } OpFunctionEnd = &Opcode { Opname: "OpFunctionEnd", + Class: "Function", + Opcode: 56, Operands: []Operand { }, } OpFunctionCall = &Opcode { Opname: "OpFunctionCall", + Class: "Function", + Opcode: 57, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1795,6 +1899,8 @@ var ( } OpVariable = &Opcode { Opname: "OpVariable", + Class: "Memory", + Opcode: 59, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1820,6 +1926,8 @@ var ( } OpImageTexelPointer = &Opcode { Opname: "OpImageTexelPointer", + Class: "Memory", + Opcode: 60, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1850,6 +1958,8 @@ var ( } OpLoad = &Opcode { Opname: "OpLoad", + Class: "Memory", + Opcode: 61, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1875,6 +1985,8 @@ var ( } OpStore = &Opcode { Opname: "OpStore", + Class: "Memory", + Opcode: 62, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1895,6 +2007,8 @@ var ( } OpCopyMemory = &Opcode { Opname: "OpCopyMemory", + Class: "Memory", + Opcode: 63, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1920,6 +2034,8 @@ var ( } OpCopyMemorySized = &Opcode { Opname: "OpCopyMemorySized", + Class: "Memory", + Opcode: 64, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -1950,6 +2066,8 @@ var ( } OpAccessChain = &Opcode { Opname: "OpAccessChain", + Class: "Memory", + Opcode: 65, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -1975,6 +2093,8 @@ var ( } OpInBoundsAccessChain = &Opcode { Opname: "OpInBoundsAccessChain", + Class: "Memory", + Opcode: 66, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2000,6 +2120,8 @@ var ( } OpPtrAccessChain = &Opcode { Opname: "OpPtrAccessChain", + Class: "Memory", + Opcode: 67, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2030,6 +2152,8 @@ var ( } OpArrayLength = &Opcode { Opname: "OpArrayLength", + Class: "Memory", + Opcode: 68, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2055,6 +2179,8 @@ var ( } OpGenericPtrMemSemantics = &Opcode { Opname: "OpGenericPtrMemSemantics", + Class: "Memory", + Opcode: 69, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2075,6 +2201,8 @@ var ( } OpInBoundsPtrAccessChain = &Opcode { Opname: "OpInBoundsPtrAccessChain", + Class: "Memory", + Opcode: 70, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2105,6 +2233,8 @@ var ( } OpDecorate = &Opcode { Opname: "OpDecorate", + Class: "Annotation", + Opcode: 71, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -2120,6 +2250,8 @@ var ( } OpMemberDecorate = &Opcode { Opname: "OpMemberDecorate", + Class: "Annotation", + Opcode: 72, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -2140,6 +2272,8 @@ var ( } OpDecorationGroup = &Opcode { Opname: "OpDecorationGroup", + Class: "Annotation", + Opcode: 73, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -2150,6 +2284,8 @@ var ( } OpGroupDecorate = &Opcode { Opname: "OpGroupDecorate", + Class: "Annotation", + Opcode: 74, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -2165,6 +2301,8 @@ var ( } OpGroupMemberDecorate = &Opcode { Opname: "OpGroupMemberDecorate", + Class: "Annotation", + Opcode: 75, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -2180,6 +2318,8 @@ var ( } OpVectorExtractDynamic = &Opcode { Opname: "OpVectorExtractDynamic", + Class: "Composite", + Opcode: 77, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2205,6 +2345,8 @@ var ( } OpVectorInsertDynamic = &Opcode { Opname: "OpVectorInsertDynamic", + Class: "Composite", + Opcode: 78, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2235,6 +2377,8 @@ var ( } OpVectorShuffle = &Opcode { Opname: "OpVectorShuffle", + Class: "Composite", + Opcode: 79, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2265,6 +2409,8 @@ var ( } OpCompositeConstruct = &Opcode { Opname: "OpCompositeConstruct", + Class: "Composite", + Opcode: 80, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2285,6 +2431,8 @@ var ( } OpCompositeExtract = &Opcode { Opname: "OpCompositeExtract", + Class: "Composite", + Opcode: 81, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2310,6 +2458,8 @@ var ( } OpCompositeInsert = &Opcode { Opname: "OpCompositeInsert", + Class: "Composite", + Opcode: 82, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2340,6 +2490,8 @@ var ( } OpCopyObject = &Opcode { Opname: "OpCopyObject", + Class: "Composite", + Opcode: 83, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2360,6 +2512,8 @@ var ( } OpTranspose = &Opcode { Opname: "OpTranspose", + Class: "Composite", + Opcode: 84, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2380,6 +2534,8 @@ var ( } OpSampledImage = &Opcode { Opname: "OpSampledImage", + Class: "Image", + Opcode: 86, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2405,6 +2561,8 @@ var ( } OpImageSampleImplicitLod = &Opcode { Opname: "OpImageSampleImplicitLod", + Class: "Image", + Opcode: 87, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2435,6 +2593,8 @@ var ( } OpImageSampleExplicitLod = &Opcode { Opname: "OpImageSampleExplicitLod", + Class: "Image", + Opcode: 88, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2465,6 +2625,8 @@ var ( } OpImageSampleDrefImplicitLod = &Opcode { Opname: "OpImageSampleDrefImplicitLod", + Class: "Image", + Opcode: 89, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2500,6 +2662,8 @@ var ( } OpImageSampleDrefExplicitLod = &Opcode { Opname: "OpImageSampleDrefExplicitLod", + Class: "Image", + Opcode: 90, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2535,6 +2699,8 @@ var ( } OpImageSampleProjImplicitLod = &Opcode { Opname: "OpImageSampleProjImplicitLod", + Class: "Image", + Opcode: 91, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2565,6 +2731,8 @@ var ( } OpImageSampleProjExplicitLod = &Opcode { Opname: "OpImageSampleProjExplicitLod", + Class: "Image", + Opcode: 92, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2595,6 +2763,8 @@ var ( } OpImageSampleProjDrefImplicitLod = &Opcode { Opname: "OpImageSampleProjDrefImplicitLod", + Class: "Image", + Opcode: 93, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2630,6 +2800,8 @@ var ( } OpImageSampleProjDrefExplicitLod = &Opcode { Opname: "OpImageSampleProjDrefExplicitLod", + Class: "Image", + Opcode: 94, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2665,6 +2837,8 @@ var ( } OpImageFetch = &Opcode { Opname: "OpImageFetch", + Class: "Image", + Opcode: 95, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2695,6 +2869,8 @@ var ( } OpImageGather = &Opcode { Opname: "OpImageGather", + Class: "Image", + Opcode: 96, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2730,6 +2906,8 @@ var ( } OpImageDrefGather = &Opcode { Opname: "OpImageDrefGather", + Class: "Image", + Opcode: 97, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2765,6 +2943,8 @@ var ( } OpImageRead = &Opcode { Opname: "OpImageRead", + Class: "Image", + Opcode: 98, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2795,6 +2975,8 @@ var ( } OpImageWrite = &Opcode { Opname: "OpImageWrite", + Class: "Image", + Opcode: 99, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -2820,6 +3002,8 @@ var ( } OpImage = &Opcode { Opname: "OpImage", + Class: "Image", + Opcode: 100, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2840,6 +3024,8 @@ var ( } OpImageQueryFormat = &Opcode { Opname: "OpImageQueryFormat", + Class: "Image", + Opcode: 101, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2860,6 +3046,8 @@ var ( } OpImageQueryOrder = &Opcode { Opname: "OpImageQueryOrder", + Class: "Image", + Opcode: 102, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2880,6 +3068,8 @@ var ( } OpImageQuerySizeLod = &Opcode { Opname: "OpImageQuerySizeLod", + Class: "Image", + Opcode: 103, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2905,6 +3095,8 @@ var ( } OpImageQuerySize = &Opcode { Opname: "OpImageQuerySize", + Class: "Image", + Opcode: 104, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2925,6 +3117,8 @@ var ( } OpImageQueryLod = &Opcode { Opname: "OpImageQueryLod", + Class: "Image", + Opcode: 105, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2950,6 +3144,8 @@ var ( } OpImageQueryLevels = &Opcode { Opname: "OpImageQueryLevels", + Class: "Image", + Opcode: 106, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2970,6 +3166,8 @@ var ( } OpImageQuerySamples = &Opcode { Opname: "OpImageQuerySamples", + Class: "Image", + Opcode: 107, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -2990,6 +3188,8 @@ var ( } OpConvertFToU = &Opcode { Opname: "OpConvertFToU", + Class: "Conversion", + Opcode: 109, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3010,6 +3210,8 @@ var ( } OpConvertFToS = &Opcode { Opname: "OpConvertFToS", + Class: "Conversion", + Opcode: 110, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3030,6 +3232,8 @@ var ( } OpConvertSToF = &Opcode { Opname: "OpConvertSToF", + Class: "Conversion", + Opcode: 111, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3050,6 +3254,8 @@ var ( } OpConvertUToF = &Opcode { Opname: "OpConvertUToF", + Class: "Conversion", + Opcode: 112, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3070,6 +3276,8 @@ var ( } OpUConvert = &Opcode { Opname: "OpUConvert", + Class: "Conversion", + Opcode: 113, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3090,6 +3298,8 @@ var ( } OpSConvert = &Opcode { Opname: "OpSConvert", + Class: "Conversion", + Opcode: 114, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3110,6 +3320,8 @@ var ( } OpFConvert = &Opcode { Opname: "OpFConvert", + Class: "Conversion", + Opcode: 115, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3130,6 +3342,8 @@ var ( } OpQuantizeToF16 = &Opcode { Opname: "OpQuantizeToF16", + Class: "Conversion", + Opcode: 116, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3150,6 +3364,8 @@ var ( } OpConvertPtrToU = &Opcode { Opname: "OpConvertPtrToU", + Class: "Conversion", + Opcode: 117, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3170,6 +3386,8 @@ var ( } OpSatConvertSToU = &Opcode { Opname: "OpSatConvertSToU", + Class: "Conversion", + Opcode: 118, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3190,6 +3408,8 @@ var ( } OpSatConvertUToS = &Opcode { Opname: "OpSatConvertUToS", + Class: "Conversion", + Opcode: 119, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3210,6 +3430,8 @@ var ( } OpConvertUToPtr = &Opcode { Opname: "OpConvertUToPtr", + Class: "Conversion", + Opcode: 120, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3230,6 +3452,8 @@ var ( } OpPtrCastToGeneric = &Opcode { Opname: "OpPtrCastToGeneric", + Class: "Conversion", + Opcode: 121, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3250,6 +3474,8 @@ var ( } OpGenericCastToPtr = &Opcode { Opname: "OpGenericCastToPtr", + Class: "Conversion", + Opcode: 122, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3270,6 +3496,8 @@ var ( } OpGenericCastToPtrExplicit = &Opcode { Opname: "OpGenericCastToPtrExplicit", + Class: "Conversion", + Opcode: 123, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3295,6 +3523,8 @@ var ( } OpBitcast = &Opcode { Opname: "OpBitcast", + Class: "Conversion", + Opcode: 124, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3315,6 +3545,8 @@ var ( } OpSNegate = &Opcode { Opname: "OpSNegate", + Class: "Arithmetic", + Opcode: 126, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3335,6 +3567,8 @@ var ( } OpFNegate = &Opcode { Opname: "OpFNegate", + Class: "Arithmetic", + Opcode: 127, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3355,6 +3589,8 @@ var ( } OpIAdd = &Opcode { Opname: "OpIAdd", + Class: "Arithmetic", + Opcode: 128, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3380,6 +3616,8 @@ var ( } OpFAdd = &Opcode { Opname: "OpFAdd", + Class: "Arithmetic", + Opcode: 129, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3405,6 +3643,8 @@ var ( } OpISub = &Opcode { Opname: "OpISub", + Class: "Arithmetic", + Opcode: 130, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3430,6 +3670,8 @@ var ( } OpFSub = &Opcode { Opname: "OpFSub", + Class: "Arithmetic", + Opcode: 131, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3455,6 +3697,8 @@ var ( } OpIMul = &Opcode { Opname: "OpIMul", + Class: "Arithmetic", + Opcode: 132, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3480,6 +3724,8 @@ var ( } OpFMul = &Opcode { Opname: "OpFMul", + Class: "Arithmetic", + Opcode: 133, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3505,6 +3751,8 @@ var ( } OpUDiv = &Opcode { Opname: "OpUDiv", + Class: "Arithmetic", + Opcode: 134, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3530,6 +3778,8 @@ var ( } OpSDiv = &Opcode { Opname: "OpSDiv", + Class: "Arithmetic", + Opcode: 135, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3555,6 +3805,8 @@ var ( } OpFDiv = &Opcode { Opname: "OpFDiv", + Class: "Arithmetic", + Opcode: 136, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3580,6 +3832,8 @@ var ( } OpUMod = &Opcode { Opname: "OpUMod", + Class: "Arithmetic", + Opcode: 137, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3605,6 +3859,8 @@ var ( } OpSRem = &Opcode { Opname: "OpSRem", + Class: "Arithmetic", + Opcode: 138, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3630,6 +3886,8 @@ var ( } OpSMod = &Opcode { Opname: "OpSMod", + Class: "Arithmetic", + Opcode: 139, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3655,6 +3913,8 @@ var ( } OpFRem = &Opcode { Opname: "OpFRem", + Class: "Arithmetic", + Opcode: 140, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3680,6 +3940,8 @@ var ( } OpFMod = &Opcode { Opname: "OpFMod", + Class: "Arithmetic", + Opcode: 141, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3705,6 +3967,8 @@ var ( } OpVectorTimesScalar = &Opcode { Opname: "OpVectorTimesScalar", + Class: "Arithmetic", + Opcode: 142, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3730,6 +3994,8 @@ var ( } OpMatrixTimesScalar = &Opcode { Opname: "OpMatrixTimesScalar", + Class: "Arithmetic", + Opcode: 143, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3755,6 +4021,8 @@ var ( } OpVectorTimesMatrix = &Opcode { Opname: "OpVectorTimesMatrix", + Class: "Arithmetic", + Opcode: 144, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3780,6 +4048,8 @@ var ( } OpMatrixTimesVector = &Opcode { Opname: "OpMatrixTimesVector", + Class: "Arithmetic", + Opcode: 145, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3805,6 +4075,8 @@ var ( } OpMatrixTimesMatrix = &Opcode { Opname: "OpMatrixTimesMatrix", + Class: "Arithmetic", + Opcode: 146, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3830,6 +4102,8 @@ var ( } OpOuterProduct = &Opcode { Opname: "OpOuterProduct", + Class: "Arithmetic", + Opcode: 147, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3855,6 +4129,8 @@ var ( } OpDot = &Opcode { Opname: "OpDot", + Class: "Arithmetic", + Opcode: 148, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3880,6 +4156,8 @@ var ( } OpIAddCarry = &Opcode { Opname: "OpIAddCarry", + Class: "Arithmetic", + Opcode: 149, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3905,6 +4183,8 @@ var ( } OpISubBorrow = &Opcode { Opname: "OpISubBorrow", + Class: "Arithmetic", + Opcode: 150, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3930,6 +4210,8 @@ var ( } OpUMulExtended = &Opcode { Opname: "OpUMulExtended", + Class: "Arithmetic", + Opcode: 151, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3955,6 +4237,8 @@ var ( } OpSMulExtended = &Opcode { Opname: "OpSMulExtended", + Class: "Arithmetic", + Opcode: 152, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -3980,6 +4264,8 @@ var ( } OpAny = &Opcode { Opname: "OpAny", + Class: "Relational_and_Logical", + Opcode: 154, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4000,6 +4286,8 @@ var ( } OpAll = &Opcode { Opname: "OpAll", + Class: "Relational_and_Logical", + Opcode: 155, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4020,6 +4308,8 @@ var ( } OpIsNan = &Opcode { Opname: "OpIsNan", + Class: "Relational_and_Logical", + Opcode: 156, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4040,6 +4330,8 @@ var ( } OpIsInf = &Opcode { Opname: "OpIsInf", + Class: "Relational_and_Logical", + Opcode: 157, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4060,6 +4352,8 @@ var ( } OpIsFinite = &Opcode { Opname: "OpIsFinite", + Class: "Relational_and_Logical", + Opcode: 158, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4080,6 +4374,8 @@ var ( } OpIsNormal = &Opcode { Opname: "OpIsNormal", + Class: "Relational_and_Logical", + Opcode: 159, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4100,6 +4396,8 @@ var ( } OpSignBitSet = &Opcode { Opname: "OpSignBitSet", + Class: "Relational_and_Logical", + Opcode: 160, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4120,6 +4418,8 @@ var ( } OpLessOrGreater = &Opcode { Opname: "OpLessOrGreater", + Class: "Relational_and_Logical", + Opcode: 161, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4145,6 +4445,8 @@ var ( } OpOrdered = &Opcode { Opname: "OpOrdered", + Class: "Relational_and_Logical", + Opcode: 162, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4170,6 +4472,8 @@ var ( } OpUnordered = &Opcode { Opname: "OpUnordered", + Class: "Relational_and_Logical", + Opcode: 163, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4195,6 +4499,8 @@ var ( } OpLogicalEqual = &Opcode { Opname: "OpLogicalEqual", + Class: "Relational_and_Logical", + Opcode: 164, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4220,6 +4526,8 @@ var ( } OpLogicalNotEqual = &Opcode { Opname: "OpLogicalNotEqual", + Class: "Relational_and_Logical", + Opcode: 165, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4245,6 +4553,8 @@ var ( } OpLogicalOr = &Opcode { Opname: "OpLogicalOr", + Class: "Relational_and_Logical", + Opcode: 166, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4270,6 +4580,8 @@ var ( } OpLogicalAnd = &Opcode { Opname: "OpLogicalAnd", + Class: "Relational_and_Logical", + Opcode: 167, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4295,6 +4607,8 @@ var ( } OpLogicalNot = &Opcode { Opname: "OpLogicalNot", + Class: "Relational_and_Logical", + Opcode: 168, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4315,6 +4629,8 @@ var ( } OpSelect = &Opcode { Opname: "OpSelect", + Class: "Relational_and_Logical", + Opcode: 169, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4345,6 +4661,8 @@ var ( } OpIEqual = &Opcode { Opname: "OpIEqual", + Class: "Relational_and_Logical", + Opcode: 170, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4370,6 +4688,8 @@ var ( } OpINotEqual = &Opcode { Opname: "OpINotEqual", + Class: "Relational_and_Logical", + Opcode: 171, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4395,6 +4715,8 @@ var ( } OpUGreaterThan = &Opcode { Opname: "OpUGreaterThan", + Class: "Relational_and_Logical", + Opcode: 172, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4420,6 +4742,8 @@ var ( } OpSGreaterThan = &Opcode { Opname: "OpSGreaterThan", + Class: "Relational_and_Logical", + Opcode: 173, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4445,6 +4769,8 @@ var ( } OpUGreaterThanEqual = &Opcode { Opname: "OpUGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 174, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4470,6 +4796,8 @@ var ( } OpSGreaterThanEqual = &Opcode { Opname: "OpSGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 175, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4495,6 +4823,8 @@ var ( } OpULessThan = &Opcode { Opname: "OpULessThan", + Class: "Relational_and_Logical", + Opcode: 176, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4520,6 +4850,8 @@ var ( } OpSLessThan = &Opcode { Opname: "OpSLessThan", + Class: "Relational_and_Logical", + Opcode: 177, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4545,6 +4877,8 @@ var ( } OpULessThanEqual = &Opcode { Opname: "OpULessThanEqual", + Class: "Relational_and_Logical", + Opcode: 178, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4570,6 +4904,8 @@ var ( } OpSLessThanEqual = &Opcode { Opname: "OpSLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 179, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4595,6 +4931,8 @@ var ( } OpFOrdEqual = &Opcode { Opname: "OpFOrdEqual", + Class: "Relational_and_Logical", + Opcode: 180, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4620,6 +4958,8 @@ var ( } OpFUnordEqual = &Opcode { Opname: "OpFUnordEqual", + Class: "Relational_and_Logical", + Opcode: 181, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4645,6 +4985,8 @@ var ( } OpFOrdNotEqual = &Opcode { Opname: "OpFOrdNotEqual", + Class: "Relational_and_Logical", + Opcode: 182, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4670,6 +5012,8 @@ var ( } OpFUnordNotEqual = &Opcode { Opname: "OpFUnordNotEqual", + Class: "Relational_and_Logical", + Opcode: 183, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4695,6 +5039,8 @@ var ( } OpFOrdLessThan = &Opcode { Opname: "OpFOrdLessThan", + Class: "Relational_and_Logical", + Opcode: 184, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4720,6 +5066,8 @@ var ( } OpFUnordLessThan = &Opcode { Opname: "OpFUnordLessThan", + Class: "Relational_and_Logical", + Opcode: 185, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4745,6 +5093,8 @@ var ( } OpFOrdGreaterThan = &Opcode { Opname: "OpFOrdGreaterThan", + Class: "Relational_and_Logical", + Opcode: 186, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4770,6 +5120,8 @@ var ( } OpFUnordGreaterThan = &Opcode { Opname: "OpFUnordGreaterThan", + Class: "Relational_and_Logical", + Opcode: 187, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4795,6 +5147,8 @@ var ( } OpFOrdLessThanEqual = &Opcode { Opname: "OpFOrdLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 188, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4820,6 +5174,8 @@ var ( } OpFUnordLessThanEqual = &Opcode { Opname: "OpFUnordLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 189, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4845,6 +5201,8 @@ var ( } OpFOrdGreaterThanEqual = &Opcode { Opname: "OpFOrdGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 190, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4870,6 +5228,8 @@ var ( } OpFUnordGreaterThanEqual = &Opcode { Opname: "OpFUnordGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 191, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4895,6 +5255,8 @@ var ( } OpShiftRightLogical = &Opcode { Opname: "OpShiftRightLogical", + Class: "Bit", + Opcode: 194, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4920,6 +5282,8 @@ var ( } OpShiftRightArithmetic = &Opcode { Opname: "OpShiftRightArithmetic", + Class: "Bit", + Opcode: 195, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4945,6 +5309,8 @@ var ( } OpShiftLeftLogical = &Opcode { Opname: "OpShiftLeftLogical", + Class: "Bit", + Opcode: 196, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4970,6 +5336,8 @@ var ( } OpBitwiseOr = &Opcode { Opname: "OpBitwiseOr", + Class: "Bit", + Opcode: 197, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -4995,6 +5363,8 @@ var ( } OpBitwiseXor = &Opcode { Opname: "OpBitwiseXor", + Class: "Bit", + Opcode: 198, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5020,6 +5390,8 @@ var ( } OpBitwiseAnd = &Opcode { Opname: "OpBitwiseAnd", + Class: "Bit", + Opcode: 199, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5045,6 +5417,8 @@ var ( } OpNot = &Opcode { Opname: "OpNot", + Class: "Bit", + Opcode: 200, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5065,6 +5439,8 @@ var ( } OpBitFieldInsert = &Opcode { Opname: "OpBitFieldInsert", + Class: "Bit", + Opcode: 201, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5100,6 +5476,8 @@ var ( } OpBitFieldSExtract = &Opcode { Opname: "OpBitFieldSExtract", + Class: "Bit", + Opcode: 202, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5130,6 +5508,8 @@ var ( } OpBitFieldUExtract = &Opcode { Opname: "OpBitFieldUExtract", + Class: "Bit", + Opcode: 203, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5160,6 +5540,8 @@ var ( } OpBitReverse = &Opcode { Opname: "OpBitReverse", + Class: "Bit", + Opcode: 204, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5180,6 +5562,8 @@ var ( } OpBitCount = &Opcode { Opname: "OpBitCount", + Class: "Bit", + Opcode: 205, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5200,6 +5584,8 @@ var ( } OpDPdx = &Opcode { Opname: "OpDPdx", + Class: "Derivative", + Opcode: 207, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5220,6 +5606,8 @@ var ( } OpDPdy = &Opcode { Opname: "OpDPdy", + Class: "Derivative", + Opcode: 208, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5240,6 +5628,8 @@ var ( } OpFwidth = &Opcode { Opname: "OpFwidth", + Class: "Derivative", + Opcode: 209, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5260,6 +5650,8 @@ var ( } OpDPdxFine = &Opcode { Opname: "OpDPdxFine", + Class: "Derivative", + Opcode: 210, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5280,6 +5672,8 @@ var ( } OpDPdyFine = &Opcode { Opname: "OpDPdyFine", + Class: "Derivative", + Opcode: 211, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5300,6 +5694,8 @@ var ( } OpFwidthFine = &Opcode { Opname: "OpFwidthFine", + Class: "Derivative", + Opcode: 212, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5320,6 +5716,8 @@ var ( } OpDPdxCoarse = &Opcode { Opname: "OpDPdxCoarse", + Class: "Derivative", + Opcode: 213, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5340,6 +5738,8 @@ var ( } OpDPdyCoarse = &Opcode { Opname: "OpDPdyCoarse", + Class: "Derivative", + Opcode: 214, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5360,6 +5760,8 @@ var ( } OpFwidthCoarse = &Opcode { Opname: "OpFwidthCoarse", + Class: "Derivative", + Opcode: 215, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5380,16 +5782,22 @@ var ( } OpEmitVertex = &Opcode { Opname: "OpEmitVertex", + Class: "Primitive", + Opcode: 218, Operands: []Operand { }, } OpEndPrimitive = &Opcode { Opname: "OpEndPrimitive", + Class: "Primitive", + Opcode: 219, Operands: []Operand { }, } OpEmitStreamVertex = &Opcode { Opname: "OpEmitStreamVertex", + Class: "Primitive", + Opcode: 220, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -5400,6 +5808,8 @@ var ( } OpEndStreamPrimitive = &Opcode { Opname: "OpEndStreamPrimitive", + Class: "Primitive", + Opcode: 221, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -5410,6 +5820,8 @@ var ( } OpControlBarrier = &Opcode { Opname: "OpControlBarrier", + Class: "Barrier", + Opcode: 224, Operands: []Operand { Operand { Kind: OperandKindIdScope, @@ -5430,6 +5842,8 @@ var ( } OpMemoryBarrier = &Opcode { Opname: "OpMemoryBarrier", + Class: "Barrier", + Opcode: 225, Operands: []Operand { Operand { Kind: OperandKindIdScope, @@ -5445,6 +5859,8 @@ var ( } OpAtomicLoad = &Opcode { Opname: "OpAtomicLoad", + Class: "Atomic", + Opcode: 227, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5475,6 +5891,8 @@ var ( } OpAtomicStore = &Opcode { Opname: "OpAtomicStore", + Class: "Atomic", + Opcode: 228, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -5500,6 +5918,8 @@ var ( } OpAtomicExchange = &Opcode { Opname: "OpAtomicExchange", + Class: "Atomic", + Opcode: 229, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5535,6 +5955,8 @@ var ( } OpAtomicCompareExchange = &Opcode { Opname: "OpAtomicCompareExchange", + Class: "Atomic", + Opcode: 230, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5580,6 +6002,8 @@ var ( } OpAtomicCompareExchangeWeak = &Opcode { Opname: "OpAtomicCompareExchangeWeak", + Class: "Atomic", + Opcode: 231, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5625,6 +6049,8 @@ var ( } OpAtomicIIncrement = &Opcode { Opname: "OpAtomicIIncrement", + Class: "Atomic", + Opcode: 232, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5655,6 +6081,8 @@ var ( } OpAtomicIDecrement = &Opcode { Opname: "OpAtomicIDecrement", + Class: "Atomic", + Opcode: 233, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5685,6 +6113,8 @@ var ( } OpAtomicIAdd = &Opcode { Opname: "OpAtomicIAdd", + Class: "Atomic", + Opcode: 234, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5720,6 +6150,8 @@ var ( } OpAtomicISub = &Opcode { Opname: "OpAtomicISub", + Class: "Atomic", + Opcode: 235, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5755,6 +6187,8 @@ var ( } OpAtomicSMin = &Opcode { Opname: "OpAtomicSMin", + Class: "Atomic", + Opcode: 236, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5790,6 +6224,8 @@ var ( } OpAtomicUMin = &Opcode { Opname: "OpAtomicUMin", + Class: "Atomic", + Opcode: 237, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5825,6 +6261,8 @@ var ( } OpAtomicSMax = &Opcode { Opname: "OpAtomicSMax", + Class: "Atomic", + Opcode: 238, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5860,6 +6298,8 @@ var ( } OpAtomicUMax = &Opcode { Opname: "OpAtomicUMax", + Class: "Atomic", + Opcode: 239, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5895,6 +6335,8 @@ var ( } OpAtomicAnd = &Opcode { Opname: "OpAtomicAnd", + Class: "Atomic", + Opcode: 240, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5930,6 +6372,8 @@ var ( } OpAtomicOr = &Opcode { Opname: "OpAtomicOr", + Class: "Atomic", + Opcode: 241, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -5965,6 +6409,8 @@ var ( } OpAtomicXor = &Opcode { Opname: "OpAtomicXor", + Class: "Atomic", + Opcode: 242, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6000,6 +6446,8 @@ var ( } OpPhi = &Opcode { Opname: "OpPhi", + Class: "Control-Flow", + Opcode: 245, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6020,6 +6468,8 @@ var ( } OpLoopMerge = &Opcode { Opname: "OpLoopMerge", + Class: "Control-Flow", + Opcode: 246, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6040,6 +6490,8 @@ var ( } OpSelectionMerge = &Opcode { Opname: "OpSelectionMerge", + Class: "Control-Flow", + Opcode: 247, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6055,6 +6507,8 @@ var ( } OpLabel = &Opcode { Opname: "OpLabel", + Class: "Control-Flow", + Opcode: 248, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -6065,6 +6519,8 @@ var ( } OpBranch = &Opcode { Opname: "OpBranch", + Class: "Control-Flow", + Opcode: 249, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6075,6 +6531,8 @@ var ( } OpBranchConditional = &Opcode { Opname: "OpBranchConditional", + Class: "Control-Flow", + Opcode: 250, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6100,6 +6558,8 @@ var ( } OpSwitch = &Opcode { Opname: "OpSwitch", + Class: "Control-Flow", + Opcode: 251, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6120,16 +6580,22 @@ var ( } OpKill = &Opcode { Opname: "OpKill", + Class: "Control-Flow", + Opcode: 252, Operands: []Operand { }, } OpReturn = &Opcode { Opname: "OpReturn", + Class: "Control-Flow", + Opcode: 253, Operands: []Operand { }, } OpReturnValue = &Opcode { Opname: "OpReturnValue", + Class: "Control-Flow", + Opcode: 254, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6140,11 +6606,15 @@ var ( } OpUnreachable = &Opcode { Opname: "OpUnreachable", + Class: "Control-Flow", + Opcode: 255, Operands: []Operand { }, } OpLifetimeStart = &Opcode { Opname: "OpLifetimeStart", + Class: "Control-Flow", + Opcode: 256, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6160,6 +6630,8 @@ var ( } OpLifetimeStop = &Opcode { Opname: "OpLifetimeStop", + Class: "Control-Flow", + Opcode: 257, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6175,6 +6647,8 @@ var ( } OpGroupAsyncCopy = &Opcode { Opname: "OpGroupAsyncCopy", + Class: "Group", + Opcode: 259, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6220,6 +6694,8 @@ var ( } OpGroupWaitEvents = &Opcode { Opname: "OpGroupWaitEvents", + Class: "Group", + Opcode: 260, Operands: []Operand { Operand { Kind: OperandKindIdScope, @@ -6240,6 +6716,8 @@ var ( } OpGroupAll = &Opcode { Opname: "OpGroupAll", + Class: "Group", + Opcode: 261, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6265,6 +6743,8 @@ var ( } OpGroupAny = &Opcode { Opname: "OpGroupAny", + Class: "Group", + Opcode: 262, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6290,6 +6770,8 @@ var ( } OpGroupBroadcast = &Opcode { Opname: "OpGroupBroadcast", + Class: "Group", + Opcode: 263, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6320,6 +6802,8 @@ var ( } OpGroupIAdd = &Opcode { Opname: "OpGroupIAdd", + Class: "Group", + Opcode: 264, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6350,6 +6834,8 @@ var ( } OpGroupFAdd = &Opcode { Opname: "OpGroupFAdd", + Class: "Group", + Opcode: 265, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6380,6 +6866,8 @@ var ( } OpGroupFMin = &Opcode { Opname: "OpGroupFMin", + Class: "Group", + Opcode: 266, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6410,6 +6898,8 @@ var ( } OpGroupUMin = &Opcode { Opname: "OpGroupUMin", + Class: "Group", + Opcode: 267, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6440,6 +6930,8 @@ var ( } OpGroupSMin = &Opcode { Opname: "OpGroupSMin", + Class: "Group", + Opcode: 268, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6470,6 +6962,8 @@ var ( } OpGroupFMax = &Opcode { Opname: "OpGroupFMax", + Class: "Group", + Opcode: 269, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6500,6 +6994,8 @@ var ( } OpGroupUMax = &Opcode { Opname: "OpGroupUMax", + Class: "Group", + Opcode: 270, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6530,6 +7026,8 @@ var ( } OpGroupSMax = &Opcode { Opname: "OpGroupSMax", + Class: "Group", + Opcode: 271, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6560,6 +7058,8 @@ var ( } OpReadPipe = &Opcode { Opname: "OpReadPipe", + Class: "Pipe", + Opcode: 274, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6595,6 +7095,8 @@ var ( } OpWritePipe = &Opcode { Opname: "OpWritePipe", + Class: "Pipe", + Opcode: 275, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6630,6 +7132,8 @@ var ( } OpReservedReadPipe = &Opcode { Opname: "OpReservedReadPipe", + Class: "Pipe", + Opcode: 276, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6675,6 +7179,8 @@ var ( } OpReservedWritePipe = &Opcode { Opname: "OpReservedWritePipe", + Class: "Pipe", + Opcode: 277, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6720,6 +7226,8 @@ var ( } OpReserveReadPipePackets = &Opcode { Opname: "OpReserveReadPipePackets", + Class: "Pipe", + Opcode: 278, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6755,6 +7263,8 @@ var ( } OpReserveWritePipePackets = &Opcode { Opname: "OpReserveWritePipePackets", + Class: "Pipe", + Opcode: 279, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6790,6 +7300,8 @@ var ( } OpCommitReadPipe = &Opcode { Opname: "OpCommitReadPipe", + Class: "Pipe", + Opcode: 280, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6815,6 +7327,8 @@ var ( } OpCommitWritePipe = &Opcode { Opname: "OpCommitWritePipe", + Class: "Pipe", + Opcode: 281, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -6840,6 +7354,8 @@ var ( } OpIsValidReserveId = &Opcode { Opname: "OpIsValidReserveId", + Class: "Pipe", + Opcode: 282, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6860,6 +7376,8 @@ var ( } OpGetNumPipePackets = &Opcode { Opname: "OpGetNumPipePackets", + Class: "Pipe", + Opcode: 283, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6890,6 +7408,8 @@ var ( } OpGetMaxPipePackets = &Opcode { Opname: "OpGetMaxPipePackets", + Class: "Pipe", + Opcode: 284, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6920,6 +7440,8 @@ var ( } OpGroupReserveReadPipePackets = &Opcode { Opname: "OpGroupReserveReadPipePackets", + Class: "Pipe", + Opcode: 285, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -6960,6 +7482,8 @@ var ( } OpGroupReserveWritePipePackets = &Opcode { Opname: "OpGroupReserveWritePipePackets", + Class: "Pipe", + Opcode: 286, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7000,6 +7524,8 @@ var ( } OpGroupCommitReadPipe = &Opcode { Opname: "OpGroupCommitReadPipe", + Class: "Pipe", + Opcode: 287, Operands: []Operand { Operand { Kind: OperandKindIdScope, @@ -7030,6 +7556,8 @@ var ( } OpGroupCommitWritePipe = &Opcode { Opname: "OpGroupCommitWritePipe", + Class: "Pipe", + Opcode: 288, Operands: []Operand { Operand { Kind: OperandKindIdScope, @@ -7060,6 +7588,8 @@ var ( } OpEnqueueMarker = &Opcode { Opname: "OpEnqueueMarker", + Class: "Device-Side_Enqueue", + Opcode: 291, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7095,6 +7625,8 @@ var ( } OpEnqueueKernel = &Opcode { Opname: "OpEnqueueKernel", + Class: "Device-Side_Enqueue", + Opcode: 292, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7165,6 +7697,8 @@ var ( } OpGetKernelNDrangeSubGroupCount = &Opcode { Opname: "OpGetKernelNDrangeSubGroupCount", + Class: "Device-Side_Enqueue", + Opcode: 293, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7205,6 +7739,8 @@ var ( } OpGetKernelNDrangeMaxSubGroupSize = &Opcode { Opname: "OpGetKernelNDrangeMaxSubGroupSize", + Class: "Device-Side_Enqueue", + Opcode: 294, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7245,6 +7781,8 @@ var ( } OpGetKernelWorkGroupSize = &Opcode { Opname: "OpGetKernelWorkGroupSize", + Class: "Device-Side_Enqueue", + Opcode: 295, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7280,6 +7818,8 @@ var ( } OpGetKernelPreferredWorkGroupSizeMultiple = &Opcode { Opname: "OpGetKernelPreferredWorkGroupSizeMultiple", + Class: "Device-Side_Enqueue", + Opcode: 296, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7315,6 +7855,8 @@ var ( } OpRetainEvent = &Opcode { Opname: "OpRetainEvent", + Class: "Device-Side_Enqueue", + Opcode: 297, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -7325,6 +7867,8 @@ var ( } OpReleaseEvent = &Opcode { Opname: "OpReleaseEvent", + Class: "Device-Side_Enqueue", + Opcode: 298, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -7335,6 +7879,8 @@ var ( } OpCreateUserEvent = &Opcode { Opname: "OpCreateUserEvent", + Class: "Device-Side_Enqueue", + Opcode: 299, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7350,6 +7896,8 @@ var ( } OpIsValidEvent = &Opcode { Opname: "OpIsValidEvent", + Class: "Device-Side_Enqueue", + Opcode: 300, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7370,6 +7918,8 @@ var ( } OpSetUserEventStatus = &Opcode { Opname: "OpSetUserEventStatus", + Class: "Device-Side_Enqueue", + Opcode: 301, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -7385,6 +7935,8 @@ var ( } OpCaptureEventProfilingInfo = &Opcode { Opname: "OpCaptureEventProfilingInfo", + Class: "Device-Side_Enqueue", + Opcode: 302, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -7405,6 +7957,8 @@ var ( } OpGetDefaultQueue = &Opcode { Opname: "OpGetDefaultQueue", + Class: "Device-Side_Enqueue", + Opcode: 303, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7420,6 +7974,8 @@ var ( } OpBuildNDRange = &Opcode { Opname: "OpBuildNDRange", + Class: "Device-Side_Enqueue", + Opcode: 304, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7450,6 +8006,8 @@ var ( } OpImageSparseSampleImplicitLod = &Opcode { Opname: "OpImageSparseSampleImplicitLod", + Class: "Image", + Opcode: 305, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7480,6 +8038,8 @@ var ( } OpImageSparseSampleExplicitLod = &Opcode { Opname: "OpImageSparseSampleExplicitLod", + Class: "Image", + Opcode: 306, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7510,6 +8070,8 @@ var ( } OpImageSparseSampleDrefImplicitLod = &Opcode { Opname: "OpImageSparseSampleDrefImplicitLod", + Class: "Image", + Opcode: 307, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7545,6 +8107,8 @@ var ( } OpImageSparseSampleDrefExplicitLod = &Opcode { Opname: "OpImageSparseSampleDrefExplicitLod", + Class: "Image", + Opcode: 308, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7580,6 +8144,8 @@ var ( } OpImageSparseSampleProjImplicitLod = &Opcode { Opname: "OpImageSparseSampleProjImplicitLod", + Class: "Image", + Opcode: 309, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7610,6 +8176,8 @@ var ( } OpImageSparseSampleProjExplicitLod = &Opcode { Opname: "OpImageSparseSampleProjExplicitLod", + Class: "Image", + Opcode: 310, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7640,6 +8208,8 @@ var ( } OpImageSparseSampleProjDrefImplicitLod = &Opcode { Opname: "OpImageSparseSampleProjDrefImplicitLod", + Class: "Image", + Opcode: 311, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7675,6 +8245,8 @@ var ( } OpImageSparseSampleProjDrefExplicitLod = &Opcode { Opname: "OpImageSparseSampleProjDrefExplicitLod", + Class: "Image", + Opcode: 312, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7710,6 +8282,8 @@ var ( } OpImageSparseFetch = &Opcode { Opname: "OpImageSparseFetch", + Class: "Image", + Opcode: 313, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7740,6 +8314,8 @@ var ( } OpImageSparseGather = &Opcode { Opname: "OpImageSparseGather", + Class: "Image", + Opcode: 314, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7775,6 +8351,8 @@ var ( } OpImageSparseDrefGather = &Opcode { Opname: "OpImageSparseDrefGather", + Class: "Image", + Opcode: 315, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7810,6 +8388,8 @@ var ( } OpImageSparseTexelsResident = &Opcode { Opname: "OpImageSparseTexelsResident", + Class: "Image", + Opcode: 316, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7830,11 +8410,15 @@ var ( } OpNoLine = &Opcode { Opname: "OpNoLine", + Class: "Debug", + Opcode: 317, Operands: []Operand { }, } OpAtomicFlagTestAndSet = &Opcode { Opname: "OpAtomicFlagTestAndSet", + Class: "Atomic", + Opcode: 318, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7865,6 +8449,8 @@ var ( } OpAtomicFlagClear = &Opcode { Opname: "OpAtomicFlagClear", + Class: "Atomic", + Opcode: 319, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -7885,6 +8471,8 @@ var ( } OpImageSparseRead = &Opcode { Opname: "OpImageSparseRead", + Class: "Image", + Opcode: 320, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7915,6 +8503,8 @@ var ( } OpSizeOf = &Opcode { Opname: "OpSizeOf", + Class: "Miscellaneous", + Opcode: 321, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7935,6 +8525,8 @@ var ( } OpTypePipeStorage = &Opcode { Opname: "OpTypePipeStorage", + Class: "Type-Declaration", + Opcode: 322, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -7945,6 +8537,8 @@ var ( } OpConstantPipeStorage = &Opcode { Opname: "OpConstantPipeStorage", + Class: "Pipe", + Opcode: 323, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7975,6 +8569,8 @@ var ( } OpCreatePipeFromPipeStorage = &Opcode { Opname: "OpCreatePipeFromPipeStorage", + Class: "Pipe", + Opcode: 324, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -7995,6 +8591,8 @@ var ( } OpGetKernelLocalSizeForSubgroupCount = &Opcode { Opname: "OpGetKernelLocalSizeForSubgroupCount", + Class: "Device-Side_Enqueue", + Opcode: 325, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8035,6 +8633,8 @@ var ( } OpGetKernelMaxNumSubgroups = &Opcode { Opname: "OpGetKernelMaxNumSubgroups", + Class: "Device-Side_Enqueue", + Opcode: 326, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8070,6 +8670,8 @@ var ( } OpTypeNamedBarrier = &Opcode { Opname: "OpTypeNamedBarrier", + Class: "Type-Declaration", + Opcode: 327, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -8080,6 +8682,8 @@ var ( } OpNamedBarrierInitialize = &Opcode { Opname: "OpNamedBarrierInitialize", + Class: "Barrier", + Opcode: 328, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8100,6 +8704,8 @@ var ( } OpMemoryNamedBarrier = &Opcode { Opname: "OpMemoryNamedBarrier", + Class: "Barrier", + Opcode: 329, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -8120,6 +8726,8 @@ var ( } OpModuleProcessed = &Opcode { Opname: "OpModuleProcessed", + Class: "Debug", + Opcode: 330, Operands: []Operand { Operand { Kind: OperandKindLiteralString, @@ -8130,6 +8738,8 @@ var ( } OpExecutionModeId = &Opcode { Opname: "OpExecutionModeId", + Class: "Mode-Setting", + Opcode: 331, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -8145,6 +8755,8 @@ var ( } OpDecorateId = &Opcode { Opname: "OpDecorateId", + Class: "Annotation", + Opcode: 332, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -8160,6 +8772,8 @@ var ( } OpGroupNonUniformElect = &Opcode { Opname: "OpGroupNonUniformElect", + Class: "Non-Uniform", + Opcode: 333, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8180,6 +8794,8 @@ var ( } OpGroupNonUniformAll = &Opcode { Opname: "OpGroupNonUniformAll", + Class: "Non-Uniform", + Opcode: 334, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8205,6 +8821,8 @@ var ( } OpGroupNonUniformAny = &Opcode { Opname: "OpGroupNonUniformAny", + Class: "Non-Uniform", + Opcode: 335, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8230,6 +8848,8 @@ var ( } OpGroupNonUniformAllEqual = &Opcode { Opname: "OpGroupNonUniformAllEqual", + Class: "Non-Uniform", + Opcode: 336, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8255,6 +8875,8 @@ var ( } OpGroupNonUniformBroadcast = &Opcode { Opname: "OpGroupNonUniformBroadcast", + Class: "Non-Uniform", + Opcode: 337, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8285,6 +8907,8 @@ var ( } OpGroupNonUniformBroadcastFirst = &Opcode { Opname: "OpGroupNonUniformBroadcastFirst", + Class: "Non-Uniform", + Opcode: 338, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8310,6 +8934,8 @@ var ( } OpGroupNonUniformBallot = &Opcode { Opname: "OpGroupNonUniformBallot", + Class: "Non-Uniform", + Opcode: 339, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8335,6 +8961,8 @@ var ( } OpGroupNonUniformInverseBallot = &Opcode { Opname: "OpGroupNonUniformInverseBallot", + Class: "Non-Uniform", + Opcode: 340, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8360,6 +8988,8 @@ var ( } OpGroupNonUniformBallotBitExtract = &Opcode { Opname: "OpGroupNonUniformBallotBitExtract", + Class: "Non-Uniform", + Opcode: 341, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8390,6 +9020,8 @@ var ( } OpGroupNonUniformBallotBitCount = &Opcode { Opname: "OpGroupNonUniformBallotBitCount", + Class: "Non-Uniform", + Opcode: 342, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8420,6 +9052,8 @@ var ( } OpGroupNonUniformBallotFindLSB = &Opcode { Opname: "OpGroupNonUniformBallotFindLSB", + Class: "Non-Uniform", + Opcode: 343, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8445,6 +9079,8 @@ var ( } OpGroupNonUniformBallotFindMSB = &Opcode { Opname: "OpGroupNonUniformBallotFindMSB", + Class: "Non-Uniform", + Opcode: 344, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8470,6 +9106,8 @@ var ( } OpGroupNonUniformShuffle = &Opcode { Opname: "OpGroupNonUniformShuffle", + Class: "Non-Uniform", + Opcode: 345, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8500,6 +9138,8 @@ var ( } OpGroupNonUniformShuffleXor = &Opcode { Opname: "OpGroupNonUniformShuffleXor", + Class: "Non-Uniform", + Opcode: 346, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8530,6 +9170,8 @@ var ( } OpGroupNonUniformShuffleUp = &Opcode { Opname: "OpGroupNonUniformShuffleUp", + Class: "Non-Uniform", + Opcode: 347, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8560,6 +9202,8 @@ var ( } OpGroupNonUniformShuffleDown = &Opcode { Opname: "OpGroupNonUniformShuffleDown", + Class: "Non-Uniform", + Opcode: 348, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8590,6 +9234,8 @@ var ( } OpGroupNonUniformIAdd = &Opcode { Opname: "OpGroupNonUniformIAdd", + Class: "Non-Uniform", + Opcode: 349, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8625,6 +9271,8 @@ var ( } OpGroupNonUniformFAdd = &Opcode { Opname: "OpGroupNonUniformFAdd", + Class: "Non-Uniform", + Opcode: 350, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8660,6 +9308,8 @@ var ( } OpGroupNonUniformIMul = &Opcode { Opname: "OpGroupNonUniformIMul", + Class: "Non-Uniform", + Opcode: 351, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8695,6 +9345,8 @@ var ( } OpGroupNonUniformFMul = &Opcode { Opname: "OpGroupNonUniformFMul", + Class: "Non-Uniform", + Opcode: 352, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8730,6 +9382,8 @@ var ( } OpGroupNonUniformSMin = &Opcode { Opname: "OpGroupNonUniformSMin", + Class: "Non-Uniform", + Opcode: 353, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8765,6 +9419,8 @@ var ( } OpGroupNonUniformUMin = &Opcode { Opname: "OpGroupNonUniformUMin", + Class: "Non-Uniform", + Opcode: 354, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8800,6 +9456,8 @@ var ( } OpGroupNonUniformFMin = &Opcode { Opname: "OpGroupNonUniformFMin", + Class: "Non-Uniform", + Opcode: 355, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8835,6 +9493,8 @@ var ( } OpGroupNonUniformSMax = &Opcode { Opname: "OpGroupNonUniformSMax", + Class: "Non-Uniform", + Opcode: 356, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8870,6 +9530,8 @@ var ( } OpGroupNonUniformUMax = &Opcode { Opname: "OpGroupNonUniformUMax", + Class: "Non-Uniform", + Opcode: 357, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8905,6 +9567,8 @@ var ( } OpGroupNonUniformFMax = &Opcode { Opname: "OpGroupNonUniformFMax", + Class: "Non-Uniform", + Opcode: 358, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8940,6 +9604,8 @@ var ( } OpGroupNonUniformBitwiseAnd = &Opcode { Opname: "OpGroupNonUniformBitwiseAnd", + Class: "Non-Uniform", + Opcode: 359, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -8975,6 +9641,8 @@ var ( } OpGroupNonUniformBitwiseOr = &Opcode { Opname: "OpGroupNonUniformBitwiseOr", + Class: "Non-Uniform", + Opcode: 360, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9010,6 +9678,8 @@ var ( } OpGroupNonUniformBitwiseXor = &Opcode { Opname: "OpGroupNonUniformBitwiseXor", + Class: "Non-Uniform", + Opcode: 361, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9045,6 +9715,8 @@ var ( } OpGroupNonUniformLogicalAnd = &Opcode { Opname: "OpGroupNonUniformLogicalAnd", + Class: "Non-Uniform", + Opcode: 362, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9080,6 +9752,8 @@ var ( } OpGroupNonUniformLogicalOr = &Opcode { Opname: "OpGroupNonUniformLogicalOr", + Class: "Non-Uniform", + Opcode: 363, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9115,6 +9789,8 @@ var ( } OpGroupNonUniformLogicalXor = &Opcode { Opname: "OpGroupNonUniformLogicalXor", + Class: "Non-Uniform", + Opcode: 364, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9150,6 +9826,8 @@ var ( } OpGroupNonUniformQuadBroadcast = &Opcode { Opname: "OpGroupNonUniformQuadBroadcast", + Class: "Non-Uniform", + Opcode: 365, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9180,6 +9858,8 @@ var ( } OpGroupNonUniformQuadSwap = &Opcode { Opname: "OpGroupNonUniformQuadSwap", + Class: "Non-Uniform", + Opcode: 366, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9210,6 +9890,8 @@ var ( } OpCopyLogical = &Opcode { Opname: "OpCopyLogical", + Class: "Composite", + Opcode: 400, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9230,6 +9912,8 @@ var ( } OpPtrEqual = &Opcode { Opname: "OpPtrEqual", + Class: "Memory", + Opcode: 401, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9255,6 +9939,8 @@ var ( } OpPtrNotEqual = &Opcode { Opname: "OpPtrNotEqual", + Class: "Memory", + Opcode: 402, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9280,6 +9966,8 @@ var ( } OpPtrDiff = &Opcode { Opname: "OpPtrDiff", + Class: "Memory", + Opcode: 403, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9305,6 +9993,8 @@ var ( } OpSubgroupBallotKHR = &Opcode { Opname: "OpSubgroupBallotKHR", + Class: "Group", + Opcode: 4421, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9325,6 +10015,8 @@ var ( } OpSubgroupFirstInvocationKHR = &Opcode { Opname: "OpSubgroupFirstInvocationKHR", + Class: "Group", + Opcode: 4422, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9345,6 +10037,8 @@ var ( } OpSubgroupAllKHR = &Opcode { Opname: "OpSubgroupAllKHR", + Class: "Group", + Opcode: 4428, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9365,6 +10059,8 @@ var ( } OpSubgroupAnyKHR = &Opcode { Opname: "OpSubgroupAnyKHR", + Class: "Group", + Opcode: 4429, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9385,6 +10081,8 @@ var ( } OpSubgroupAllEqualKHR = &Opcode { Opname: "OpSubgroupAllEqualKHR", + Class: "Group", + Opcode: 4430, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9405,6 +10103,8 @@ var ( } OpSubgroupReadInvocationKHR = &Opcode { Opname: "OpSubgroupReadInvocationKHR", + Class: "Group", + Opcode: 4432, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9430,6 +10130,8 @@ var ( } OpGroupIAddNonUniformAMD = &Opcode { Opname: "OpGroupIAddNonUniformAMD", + Class: "Group", + Opcode: 5000, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9460,6 +10162,8 @@ var ( } OpGroupFAddNonUniformAMD = &Opcode { Opname: "OpGroupFAddNonUniformAMD", + Class: "Group", + Opcode: 5001, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9490,6 +10194,8 @@ var ( } OpGroupFMinNonUniformAMD = &Opcode { Opname: "OpGroupFMinNonUniformAMD", + Class: "Group", + Opcode: 5002, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9520,6 +10226,8 @@ var ( } OpGroupUMinNonUniformAMD = &Opcode { Opname: "OpGroupUMinNonUniformAMD", + Class: "Group", + Opcode: 5003, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9550,6 +10258,8 @@ var ( } OpGroupSMinNonUniformAMD = &Opcode { Opname: "OpGroupSMinNonUniformAMD", + Class: "Group", + Opcode: 5004, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9580,6 +10290,8 @@ var ( } OpGroupFMaxNonUniformAMD = &Opcode { Opname: "OpGroupFMaxNonUniformAMD", + Class: "Group", + Opcode: 5005, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9610,6 +10322,8 @@ var ( } OpGroupUMaxNonUniformAMD = &Opcode { Opname: "OpGroupUMaxNonUniformAMD", + Class: "Group", + Opcode: 5006, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9640,6 +10354,8 @@ var ( } OpGroupSMaxNonUniformAMD = &Opcode { Opname: "OpGroupSMaxNonUniformAMD", + Class: "Group", + Opcode: 5007, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9670,6 +10386,8 @@ var ( } OpFragmentMaskFetchAMD = &Opcode { Opname: "OpFragmentMaskFetchAMD", + Class: "Reserved", + Opcode: 5011, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9695,6 +10413,8 @@ var ( } OpFragmentFetchAMD = &Opcode { Opname: "OpFragmentFetchAMD", + Class: "Reserved", + Opcode: 5012, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9725,6 +10445,8 @@ var ( } OpReadClockKHR = &Opcode { Opname: "OpReadClockKHR", + Class: "Reserved", + Opcode: 5056, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9745,6 +10467,8 @@ var ( } OpImageSampleFootprintNV = &Opcode { Opname: "OpImageSampleFootprintNV", + Class: "Image", + Opcode: 5283, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9785,6 +10509,8 @@ var ( } OpGroupNonUniformPartitionNV = &Opcode { Opname: "OpGroupNonUniformPartitionNV", + Class: "Non-Uniform", + Opcode: 5296, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9805,6 +10531,8 @@ var ( } OpWritePackedPrimitiveIndices4x8NV = &Opcode { Opname: "OpWritePackedPrimitiveIndices4x8NV", + Class: "Reserved", + Opcode: 5299, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -9820,6 +10548,8 @@ var ( } OpReportIntersectionNV = &Opcode { Opname: "OpReportIntersectionNV", + Class: "Reserved", + Opcode: 5334, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -9845,16 +10575,22 @@ var ( } OpIgnoreIntersectionNV = &Opcode { Opname: "OpIgnoreIntersectionNV", + Class: "Reserved", + Opcode: 5335, Operands: []Operand { }, } OpTerminateRayNV = &Opcode { Opname: "OpTerminateRayNV", + Class: "Reserved", + Opcode: 5336, Operands: []Operand { }, } OpTraceNV = &Opcode { Opname: "OpTraceNV", + Class: "Reserved", + Opcode: 5337, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -9915,6 +10651,8 @@ var ( } OpTypeAccelerationStructureNV = &Opcode { Opname: "OpTypeAccelerationStructureNV", + Class: "Reserved", + Opcode: 5341, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -9925,6 +10663,8 @@ var ( } OpExecuteCallableNV = &Opcode { Opname: "OpExecuteCallableNV", + Class: "Reserved", + Opcode: 5344, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -9940,6 +10680,8 @@ var ( } OpTypeCooperativeMatrixNV = &Opcode { Opname: "OpTypeCooperativeMatrixNV", + Class: "Reserved", + Opcode: 5358, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -9970,6 +10712,8 @@ var ( } OpCooperativeMatrixLoadNV = &Opcode { Opname: "OpCooperativeMatrixLoadNV", + Class: "Reserved", + Opcode: 5359, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10005,6 +10749,8 @@ var ( } OpCooperativeMatrixStoreNV = &Opcode { Opname: "OpCooperativeMatrixStoreNV", + Class: "Reserved", + Opcode: 5360, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10035,6 +10781,8 @@ var ( } OpCooperativeMatrixMulAddNV = &Opcode { Opname: "OpCooperativeMatrixMulAddNV", + Class: "Reserved", + Opcode: 5361, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10065,6 +10813,8 @@ var ( } OpCooperativeMatrixLengthNV = &Opcode { Opname: "OpCooperativeMatrixLengthNV", + Class: "Reserved", + Opcode: 5362, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10085,21 +10835,29 @@ var ( } OpBeginInvocationInterlockEXT = &Opcode { Opname: "OpBeginInvocationInterlockEXT", + Class: "Reserved", + Opcode: 5364, Operands: []Operand { }, } OpEndInvocationInterlockEXT = &Opcode { Opname: "OpEndInvocationInterlockEXT", + Class: "Reserved", + Opcode: 5365, Operands: []Operand { }, } OpDemoteToHelperInvocationEXT = &Opcode { Opname: "OpDemoteToHelperInvocationEXT", + Class: "Reserved", + Opcode: 5380, Operands: []Operand { }, } OpIsHelperInvocationEXT = &Opcode { Opname: "OpIsHelperInvocationEXT", + Class: "Reserved", + Opcode: 5381, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10115,6 +10873,8 @@ var ( } OpSubgroupShuffleINTEL = &Opcode { Opname: "OpSubgroupShuffleINTEL", + Class: "Group", + Opcode: 5571, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10140,6 +10900,8 @@ var ( } OpSubgroupShuffleDownINTEL = &Opcode { Opname: "OpSubgroupShuffleDownINTEL", + Class: "Group", + Opcode: 5572, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10170,6 +10932,8 @@ var ( } OpSubgroupShuffleUpINTEL = &Opcode { Opname: "OpSubgroupShuffleUpINTEL", + Class: "Group", + Opcode: 5573, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10200,6 +10964,8 @@ var ( } OpSubgroupShuffleXorINTEL = &Opcode { Opname: "OpSubgroupShuffleXorINTEL", + Class: "Group", + Opcode: 5574, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10225,6 +10991,8 @@ var ( } OpSubgroupBlockReadINTEL = &Opcode { Opname: "OpSubgroupBlockReadINTEL", + Class: "Group", + Opcode: 5575, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10245,6 +11013,8 @@ var ( } OpSubgroupBlockWriteINTEL = &Opcode { Opname: "OpSubgroupBlockWriteINTEL", + Class: "Group", + Opcode: 5576, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10260,6 +11030,8 @@ var ( } OpSubgroupImageBlockReadINTEL = &Opcode { Opname: "OpSubgroupImageBlockReadINTEL", + Class: "Group", + Opcode: 5577, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10285,6 +11057,8 @@ var ( } OpSubgroupImageBlockWriteINTEL = &Opcode { Opname: "OpSubgroupImageBlockWriteINTEL", + Class: "Group", + Opcode: 5578, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10305,6 +11079,8 @@ var ( } OpSubgroupImageMediaBlockReadINTEL = &Opcode { Opname: "OpSubgroupImageMediaBlockReadINTEL", + Class: "Group", + Opcode: 5580, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10340,6 +11116,8 @@ var ( } OpSubgroupImageMediaBlockWriteINTEL = &Opcode { Opname: "OpSubgroupImageMediaBlockWriteINTEL", + Class: "Group", + Opcode: 5581, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10370,6 +11148,8 @@ var ( } OpUCountLeadingZerosINTEL = &Opcode { Opname: "OpUCountLeadingZerosINTEL", + Class: "Reserved", + Opcode: 5585, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10390,6 +11170,8 @@ var ( } OpUCountTrailingZerosINTEL = &Opcode { Opname: "OpUCountTrailingZerosINTEL", + Class: "Reserved", + Opcode: 5586, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10410,6 +11192,8 @@ var ( } OpAbsISubINTEL = &Opcode { Opname: "OpAbsISubINTEL", + Class: "Reserved", + Opcode: 5587, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10435,6 +11219,8 @@ var ( } OpAbsUSubINTEL = &Opcode { Opname: "OpAbsUSubINTEL", + Class: "Reserved", + Opcode: 5588, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10460,6 +11246,8 @@ var ( } OpIAddSatINTEL = &Opcode { Opname: "OpIAddSatINTEL", + Class: "Reserved", + Opcode: 5589, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10485,6 +11273,8 @@ var ( } OpUAddSatINTEL = &Opcode { Opname: "OpUAddSatINTEL", + Class: "Reserved", + Opcode: 5590, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10510,6 +11300,8 @@ var ( } OpIAverageINTEL = &Opcode { Opname: "OpIAverageINTEL", + Class: "Reserved", + Opcode: 5591, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10535,6 +11327,8 @@ var ( } OpUAverageINTEL = &Opcode { Opname: "OpUAverageINTEL", + Class: "Reserved", + Opcode: 5592, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10560,6 +11354,8 @@ var ( } OpIAverageRoundedINTEL = &Opcode { Opname: "OpIAverageRoundedINTEL", + Class: "Reserved", + Opcode: 5593, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10585,6 +11381,8 @@ var ( } OpUAverageRoundedINTEL = &Opcode { Opname: "OpUAverageRoundedINTEL", + Class: "Reserved", + Opcode: 5594, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10610,6 +11408,8 @@ var ( } OpISubSatINTEL = &Opcode { Opname: "OpISubSatINTEL", + Class: "Reserved", + Opcode: 5595, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10635,6 +11435,8 @@ var ( } OpUSubSatINTEL = &Opcode { Opname: "OpUSubSatINTEL", + Class: "Reserved", + Opcode: 5596, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10660,6 +11462,8 @@ var ( } OpIMul32x16INTEL = &Opcode { Opname: "OpIMul32x16INTEL", + Class: "Reserved", + Opcode: 5597, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10685,6 +11489,8 @@ var ( } OpUMul32x16INTEL = &Opcode { Opname: "OpUMul32x16INTEL", + Class: "Reserved", + Opcode: 5598, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10710,6 +11516,8 @@ var ( } OpDecorateString = &Opcode { Opname: "OpDecorateString", + Class: "Annotation", + Opcode: 5632, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10725,6 +11533,8 @@ var ( } OpDecorateStringGOOGLE = &Opcode { Opname: "OpDecorateStringGOOGLE", + Class: "Annotation", + Opcode: 5632, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10740,6 +11550,8 @@ var ( } OpMemberDecorateString = &Opcode { Opname: "OpMemberDecorateString", + Class: "Annotation", + Opcode: 5633, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10760,6 +11572,8 @@ var ( } OpMemberDecorateStringGOOGLE = &Opcode { Opname: "OpMemberDecorateStringGOOGLE", + Class: "Annotation", + Opcode: 5633, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10780,6 +11594,8 @@ var ( } OpVmeImageINTEL = &Opcode { Opname: "OpVmeImageINTEL", + Class: "@exclude", + Opcode: 5699, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10805,6 +11621,8 @@ var ( } OpTypeVmeImageINTEL = &Opcode { Opname: "OpTypeVmeImageINTEL", + Class: "@exclude", + Opcode: 5700, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10820,6 +11638,8 @@ var ( } OpTypeAvcImePayloadINTEL = &Opcode { Opname: "OpTypeAvcImePayloadINTEL", + Class: "@exclude", + Opcode: 5701, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10830,6 +11650,8 @@ var ( } OpTypeAvcRefPayloadINTEL = &Opcode { Opname: "OpTypeAvcRefPayloadINTEL", + Class: "@exclude", + Opcode: 5702, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10840,6 +11662,8 @@ var ( } OpTypeAvcSicPayloadINTEL = &Opcode { Opname: "OpTypeAvcSicPayloadINTEL", + Class: "@exclude", + Opcode: 5703, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10850,6 +11674,8 @@ var ( } OpTypeAvcMcePayloadINTEL = &Opcode { Opname: "OpTypeAvcMcePayloadINTEL", + Class: "@exclude", + Opcode: 5704, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10860,6 +11686,8 @@ var ( } OpTypeAvcMceResultINTEL = &Opcode { Opname: "OpTypeAvcMceResultINTEL", + Class: "@exclude", + Opcode: 5705, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10870,6 +11698,8 @@ var ( } OpTypeAvcImeResultINTEL = &Opcode { Opname: "OpTypeAvcImeResultINTEL", + Class: "@exclude", + Opcode: 5706, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10880,6 +11710,8 @@ var ( } OpTypeAvcImeResultSingleReferenceStreamoutINTEL = &Opcode { Opname: "OpTypeAvcImeResultSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5707, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10890,6 +11722,8 @@ var ( } OpTypeAvcImeResultDualReferenceStreamoutINTEL = &Opcode { Opname: "OpTypeAvcImeResultDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5708, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10900,6 +11734,8 @@ var ( } OpTypeAvcImeSingleReferenceStreaminINTEL = &Opcode { Opname: "OpTypeAvcImeSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5709, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10910,6 +11746,8 @@ var ( } OpTypeAvcImeDualReferenceStreaminINTEL = &Opcode { Opname: "OpTypeAvcImeDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5710, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10920,6 +11758,8 @@ var ( } OpTypeAvcRefResultINTEL = &Opcode { Opname: "OpTypeAvcRefResultINTEL", + Class: "@exclude", + Opcode: 5711, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10930,6 +11770,8 @@ var ( } OpTypeAvcSicResultINTEL = &Opcode { Opname: "OpTypeAvcSicResultINTEL", + Class: "@exclude", + Opcode: 5712, Operands: []Operand { Operand { Kind: OperandKindIdResult, @@ -10940,6 +11782,8 @@ var ( } OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL", + Class: "@exclude", + Opcode: 5713, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10965,6 +11809,8 @@ var ( } OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL", + Class: "@exclude", + Opcode: 5714, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10990,6 +11836,8 @@ var ( } OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5715, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11015,6 +11863,8 @@ var ( } OpSubgroupAvcMceSetInterShapePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetInterShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5716, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11040,6 +11890,8 @@ var ( } OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL", + Class: "@exclude", + Opcode: 5717, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11065,6 +11917,8 @@ var ( } OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL", + Class: "@exclude", + Opcode: 5718, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11090,6 +11944,8 @@ var ( } OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5719, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11115,6 +11971,8 @@ var ( } OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL", + Class: "@exclude", + Opcode: 5720, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11140,6 +11998,8 @@ var ( } OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5721, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11155,6 +12015,8 @@ var ( } OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5722, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11170,6 +12032,8 @@ var ( } OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5723, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11185,6 +12049,8 @@ var ( } OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL", + Class: "@exclude", + Opcode: 5724, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11220,6 +12086,8 @@ var ( } OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL", + Class: "@exclude", + Opcode: 5725, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11245,6 +12113,8 @@ var ( } OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL", + Class: "@exclude", + Opcode: 5726, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11260,6 +12130,8 @@ var ( } OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL", + Class: "@exclude", + Opcode: 5727, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11275,6 +12147,8 @@ var ( } OpSubgroupAvcMceSetAcOnlyHaarINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetAcOnlyHaarINTEL", + Class: "@exclude", + Opcode: 5728, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11295,6 +12169,8 @@ var ( } OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL", + Class: "@exclude", + Opcode: 5729, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11320,6 +12196,8 @@ var ( } OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL", + Class: "@exclude", + Opcode: 5730, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11345,6 +12223,8 @@ var ( } OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = &Opcode { Opname: "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL", + Class: "@exclude", + Opcode: 5731, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11375,6 +12255,8 @@ var ( } OpSubgroupAvcMceConvertToImePayloadINTEL = &Opcode { Opname: "OpSubgroupAvcMceConvertToImePayloadINTEL", + Class: "@exclude", + Opcode: 5732, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11395,6 +12277,8 @@ var ( } OpSubgroupAvcMceConvertToImeResultINTEL = &Opcode { Opname: "OpSubgroupAvcMceConvertToImeResultINTEL", + Class: "@exclude", + Opcode: 5733, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11415,6 +12299,8 @@ var ( } OpSubgroupAvcMceConvertToRefPayloadINTEL = &Opcode { Opname: "OpSubgroupAvcMceConvertToRefPayloadINTEL", + Class: "@exclude", + Opcode: 5734, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11435,6 +12321,8 @@ var ( } OpSubgroupAvcMceConvertToRefResultINTEL = &Opcode { Opname: "OpSubgroupAvcMceConvertToRefResultINTEL", + Class: "@exclude", + Opcode: 5735, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11455,6 +12343,8 @@ var ( } OpSubgroupAvcMceConvertToSicPayloadINTEL = &Opcode { Opname: "OpSubgroupAvcMceConvertToSicPayloadINTEL", + Class: "@exclude", + Opcode: 5736, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11475,6 +12365,8 @@ var ( } OpSubgroupAvcMceConvertToSicResultINTEL = &Opcode { Opname: "OpSubgroupAvcMceConvertToSicResultINTEL", + Class: "@exclude", + Opcode: 5737, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11495,6 +12387,8 @@ var ( } OpSubgroupAvcMceGetMotionVectorsINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5738, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11515,6 +12409,8 @@ var ( } OpSubgroupAvcMceGetInterDistortionsINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetInterDistortionsINTEL", + Class: "@exclude", + Opcode: 5739, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11535,6 +12431,8 @@ var ( } OpSubgroupAvcMceGetBestInterDistortionsINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetBestInterDistortionsINTEL", + Class: "@exclude", + Opcode: 5740, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11555,6 +12453,8 @@ var ( } OpSubgroupAvcMceGetInterMajorShapeINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetInterMajorShapeINTEL", + Class: "@exclude", + Opcode: 5741, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11575,6 +12475,8 @@ var ( } OpSubgroupAvcMceGetInterMinorShapeINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetInterMinorShapeINTEL", + Class: "@exclude", + Opcode: 5742, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11595,6 +12497,8 @@ var ( } OpSubgroupAvcMceGetInterDirectionsINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetInterDirectionsINTEL", + Class: "@exclude", + Opcode: 5743, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11615,6 +12519,8 @@ var ( } OpSubgroupAvcMceGetInterMotionVectorCountINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetInterMotionVectorCountINTEL", + Class: "@exclude", + Opcode: 5744, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11635,6 +12541,8 @@ var ( } OpSubgroupAvcMceGetInterReferenceIdsINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetInterReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5745, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11655,6 +12563,8 @@ var ( } OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = &Opcode { Opname: "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL", + Class: "@exclude", + Opcode: 5746, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11685,6 +12595,8 @@ var ( } OpSubgroupAvcImeInitializeINTEL = &Opcode { Opname: "OpSubgroupAvcImeInitializeINTEL", + Class: "@exclude", + Opcode: 5747, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11715,6 +12627,8 @@ var ( } OpSubgroupAvcImeSetSingleReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcImeSetSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5748, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11745,6 +12659,8 @@ var ( } OpSubgroupAvcImeSetDualReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcImeSetDualReferenceINTEL", + Class: "@exclude", + Opcode: 5749, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11780,6 +12696,8 @@ var ( } OpSubgroupAvcImeRefWindowSizeINTEL = &Opcode { Opname: "OpSubgroupAvcImeRefWindowSizeINTEL", + Class: "@exclude", + Opcode: 5750, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11805,6 +12723,8 @@ var ( } OpSubgroupAvcImeAdjustRefOffsetINTEL = &Opcode { Opname: "OpSubgroupAvcImeAdjustRefOffsetINTEL", + Class: "@exclude", + Opcode: 5751, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11840,6 +12760,8 @@ var ( } OpSubgroupAvcImeConvertToMcePayloadINTEL = &Opcode { Opname: "OpSubgroupAvcImeConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5752, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11860,6 +12782,8 @@ var ( } OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = &Opcode { Opname: "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL", + Class: "@exclude", + Opcode: 5753, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11885,6 +12809,8 @@ var ( } OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = &Opcode { Opname: "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL", + Class: "@exclude", + Opcode: 5754, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11905,6 +12831,8 @@ var ( } OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = &Opcode { Opname: "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL", + Class: "@exclude", + Opcode: 5755, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11930,6 +12858,8 @@ var ( } OpSubgroupAvcImeSetWeightedSadINTEL = &Opcode { Opname: "OpSubgroupAvcImeSetWeightedSadINTEL", + Class: "@exclude", + Opcode: 5756, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11955,6 +12885,8 @@ var ( } OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5757, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -11985,6 +12917,8 @@ var ( } OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5758, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12020,6 +12954,8 @@ var ( } OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5759, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12055,6 +12991,8 @@ var ( } OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5760, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12095,6 +13033,8 @@ var ( } OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5761, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12125,6 +13065,8 @@ var ( } OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5762, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12160,6 +13102,8 @@ var ( } OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL", + Class: "@exclude", + Opcode: 5763, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12195,6 +13139,8 @@ var ( } OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = &Opcode { Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL", + Class: "@exclude", + Opcode: 5764, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12235,6 +13181,8 @@ var ( } OpSubgroupAvcImeConvertToMceResultINTEL = &Opcode { Opname: "OpSubgroupAvcImeConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5765, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12255,6 +13203,8 @@ var ( } OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5766, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12275,6 +13225,8 @@ var ( } OpSubgroupAvcImeGetDualReferenceStreaminINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5767, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12295,6 +13247,8 @@ var ( } OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = &Opcode { Opname: "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5768, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12315,6 +13269,8 @@ var ( } OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = &Opcode { Opname: "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5769, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12335,6 +13291,8 @@ var ( } OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5770, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12360,6 +13318,8 @@ var ( } OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL", + Class: "@exclude", + Opcode: 5771, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12385,6 +13345,8 @@ var ( } OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5772, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12410,6 +13372,8 @@ var ( } OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5773, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12440,6 +13404,8 @@ var ( } OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL", + Class: "@exclude", + Opcode: 5774, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12470,6 +13436,8 @@ var ( } OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5775, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12500,6 +13468,8 @@ var ( } OpSubgroupAvcImeGetBorderReachedINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetBorderReachedINTEL", + Class: "@exclude", + Opcode: 5776, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12525,6 +13495,8 @@ var ( } OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL", + Class: "@exclude", + Opcode: 5777, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12545,6 +13517,8 @@ var ( } OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL", + Class: "@exclude", + Opcode: 5778, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12565,6 +13539,8 @@ var ( } OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL", + Class: "@exclude", + Opcode: 5779, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12585,6 +13561,8 @@ var ( } OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = &Opcode { Opname: "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL", + Class: "@exclude", + Opcode: 5780, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12605,6 +13583,8 @@ var ( } OpSubgroupAvcFmeInitializeINTEL = &Opcode { Opname: "OpSubgroupAvcFmeInitializeINTEL", + Class: "@exclude", + Opcode: 5781, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12655,6 +13635,8 @@ var ( } OpSubgroupAvcBmeInitializeINTEL = &Opcode { Opname: "OpSubgroupAvcBmeInitializeINTEL", + Class: "@exclude", + Opcode: 5782, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12710,6 +13692,8 @@ var ( } OpSubgroupAvcRefConvertToMcePayloadINTEL = &Opcode { Opname: "OpSubgroupAvcRefConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5783, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12730,6 +13714,8 @@ var ( } OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = &Opcode { Opname: "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL", + Class: "@exclude", + Opcode: 5784, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12750,6 +13736,8 @@ var ( } OpSubgroupAvcRefSetBilinearFilterEnableINTEL = &Opcode { Opname: "OpSubgroupAvcRefSetBilinearFilterEnableINTEL", + Class: "@exclude", + Opcode: 5785, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12770,6 +13758,8 @@ var ( } OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5786, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12800,6 +13790,8 @@ var ( } OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5787, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12835,6 +13827,8 @@ var ( } OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL", + Class: "@exclude", + Opcode: 5788, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12865,6 +13859,8 @@ var ( } OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = &Opcode { Opname: "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL", + Class: "@exclude", + Opcode: 5789, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12900,6 +13896,8 @@ var ( } OpSubgroupAvcRefConvertToMceResultINTEL = &Opcode { Opname: "OpSubgroupAvcRefConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5790, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12920,6 +13918,8 @@ var ( } OpSubgroupAvcSicInitializeINTEL = &Opcode { Opname: "OpSubgroupAvcSicInitializeINTEL", + Class: "@exclude", + Opcode: 5791, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12940,6 +13940,8 @@ var ( } OpSubgroupAvcSicConfigureSkcINTEL = &Opcode { Opname: "OpSubgroupAvcSicConfigureSkcINTEL", + Class: "@exclude", + Opcode: 5792, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -12985,6 +13987,8 @@ var ( } OpSubgroupAvcSicConfigureIpeLumaINTEL = &Opcode { Opname: "OpSubgroupAvcSicConfigureIpeLumaINTEL", + Class: "@exclude", + Opcode: 5793, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13040,6 +14044,8 @@ var ( } OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = &Opcode { Opname: "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL", + Class: "@exclude", + Opcode: 5794, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13110,6 +14116,8 @@ var ( } OpSubgroupAvcSicGetMotionVectorMaskINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetMotionVectorMaskINTEL", + Class: "@exclude", + Opcode: 5795, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13135,6 +14143,8 @@ var ( } OpSubgroupAvcSicConvertToMcePayloadINTEL = &Opcode { Opname: "OpSubgroupAvcSicConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5796, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13155,6 +14165,8 @@ var ( } OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = &Opcode { Opname: "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5797, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13180,6 +14192,8 @@ var ( } OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = &Opcode { Opname: "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL", + Class: "@exclude", + Opcode: 5798, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13215,6 +14229,8 @@ var ( } OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = &Opcode { Opname: "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL", + Class: "@exclude", + Opcode: 5799, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13240,6 +14256,8 @@ var ( } OpSubgroupAvcSicSetBilinearFilterEnableINTEL = &Opcode { Opname: "OpSubgroupAvcSicSetBilinearFilterEnableINTEL", + Class: "@exclude", + Opcode: 5800, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13260,6 +14278,8 @@ var ( } OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = &Opcode { Opname: "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL", + Class: "@exclude", + Opcode: 5801, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13285,6 +14305,8 @@ var ( } OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = &Opcode { Opname: "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL", + Class: "@exclude", + Opcode: 5802, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13310,6 +14332,8 @@ var ( } OpSubgroupAvcSicEvaluateIpeINTEL = &Opcode { Opname: "OpSubgroupAvcSicEvaluateIpeINTEL", + Class: "@exclude", + Opcode: 5803, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13335,6 +14359,8 @@ var ( } OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5804, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13365,6 +14391,8 @@ var ( } OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5805, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13400,6 +14428,8 @@ var ( } OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = &Opcode { Opname: "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL", + Class: "@exclude", + Opcode: 5806, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13430,6 +14460,8 @@ var ( } OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = &Opcode { Opname: "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL", + Class: "@exclude", + Opcode: 5807, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13465,6 +14497,8 @@ var ( } OpSubgroupAvcSicConvertToMceResultINTEL = &Opcode { Opname: "OpSubgroupAvcSicConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5808, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13485,6 +14519,8 @@ var ( } OpSubgroupAvcSicGetIpeLumaShapeINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetIpeLumaShapeINTEL", + Class: "@exclude", + Opcode: 5809, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13505,6 +14541,8 @@ var ( } OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL", + Class: "@exclude", + Opcode: 5810, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13525,6 +14563,8 @@ var ( } OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL", + Class: "@exclude", + Opcode: 5811, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13545,6 +14585,8 @@ var ( } OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL", + Class: "@exclude", + Opcode: 5812, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13565,6 +14607,8 @@ var ( } OpSubgroupAvcSicGetIpeChromaModeINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetIpeChromaModeINTEL", + Class: "@exclude", + Opcode: 5813, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13585,6 +14629,8 @@ var ( } OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL", + Class: "@exclude", + Opcode: 5814, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13605,6 +14651,8 @@ var ( } OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL", + Class: "@exclude", + Opcode: 5815, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -13625,6 +14673,8 @@ var ( } OpSubgroupAvcSicGetInterRawSadsINTEL = &Opcode { Opname: "OpSubgroupAvcSicGetInterRawSadsINTEL", + Class: "@exclude", + Opcode: 5816, Operands: []Operand { Operand { Kind: OperandKindIdResultType, diff --git a/utils/vscode/src/schema/schema.go.tmpl b/utils/vscode/src/schema/schema.go.tmpl index ce5500fe8a..987fcd0627 100644 --- a/utils/vscode/src/schema/schema.go.tmpl +++ b/utils/vscode/src/schema/schema.go.tmpl @@ -115,6 +115,8 @@ var ( {{range $i := .SPIRV.Instructions}} {{Title $i.Opname}} = &Opcode { Opname: "{{$i.Opname}}", + Class: "{{$i.Class}}", + Opcode: {{$i.Opcode}}, Operands: []Operand {•{{range $i := $i.Operands}} Operand { Kind: OperandKind{{$i.Kind}}, From 03794b8f5e08ab3293988e9b48e9f12a9a9ede5e Mon Sep 17 00:00:00 2001 From: Danilo Spinella Date: Thu, 20 Feb 2020 16:54:54 +0100 Subject: [PATCH 24/88] Fix static libraries linking order (#3189) Fix #1569 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ed56a8159..ef9ad112f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,7 +300,7 @@ if (NOT "${SPIRV_SKIP_TESTS}") WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() -set(SPIRV_LIBRARIES "-lSPIRV-Tools -lSPIRV-Tools-link -lSPIRV-Tools-opt") +set(SPIRV_LIBRARIES "-lSPIRV-Tools-opt -lSPIRV-Tools -lSPIRV-Tools-link") set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared") # Build pkg-config file From 4a80497a888511834d9a1f5930226e82decc60d5 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 20 Feb 2020 14:07:57 -0500 Subject: [PATCH 25/88] Make spvOpcodeString part of the public API (#3174) Fixes #3138 --- include/spirv-tools/libspirv.h | 3 +++ source/opcode.cpp | 10 +++++++--- source/opcode.h | 3 --- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index d63f3634d0..21a9608f8c 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -766,6 +766,9 @@ SPIRV_TOOLS_EXPORT void spvDiagnosticDestroy(spv_diagnostic diagnostic); SPIRV_TOOLS_EXPORT spv_result_t spvDiagnosticPrint(const spv_diagnostic diagnostic); +// Gets the name of an instruction, without the "Op" prefix. +SPIRV_TOOLS_EXPORT const char* spvOpcodeString(const uint32_t opcode); + // The binary parser interface. // A pointer to a function that accepts a parsed SPIR-V header. diff --git a/source/opcode.cpp b/source/opcode.cpp index 7f91a0ffd4..b03db165ce 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -181,11 +181,15 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode, } } -const char* spvOpcodeString(const SpvOp opcode) { +const char* spvOpcodeString(const uint32_t opcode) { const auto beg = kOpcodeTableEntries; const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries); - spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, - false, false, 0, nullptr, ~0u, ~0u}; + spv_opcode_desc_t needle = {"", static_cast(opcode), + 0, nullptr, + 0, {}, + false, false, + 0, nullptr, + ~0u, ~0u}; auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { return lhs.opcode < rhs.opcode; }; diff --git a/source/opcode.h b/source/opcode.h index ed64f1b5ab..f79826fcde 100644 --- a/source/opcode.h +++ b/source/opcode.h @@ -56,9 +56,6 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode, const uint16_t word_count, const spv_endianness_t endian, spv_instruction_t* inst); -// Gets the name of an instruction, without the "Op" prefix. -const char* spvOpcodeString(const SpvOp opcode); - // Determine if the given opcode is a scalar type. Returns zero if false, // non-zero otherwise. int32_t spvOpcodeIsScalarType(const SpvOp opcode); From dea1040fa4a94d0d79e4bce9b8c18b7c15752a4a Mon Sep 17 00:00:00 2001 From: Ricardo Garcia <47594367+rg3igalia@users.noreply.github.com> Date: Fri, 21 Feb 2020 21:06:46 +0100 Subject: [PATCH 26/88] Fix ignored const qualifier warning in static_cast (#3197) --- source/opcode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/opcode.cpp b/source/opcode.cpp index b03db165ce..a837b95121 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -184,7 +184,7 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode, const char* spvOpcodeString(const uint32_t opcode) { const auto beg = kOpcodeTableEntries; const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries); - spv_opcode_desc_t needle = {"", static_cast(opcode), + spv_opcode_desc_t needle = {"", static_cast(opcode), 0, nullptr, 0, {}, false, false, From 03c9effb58970ab1c99584ac41a02875b74846d3 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Fri, 21 Feb 2020 22:23:08 +0000 Subject: [PATCH 27/88] Brief guide to writing a spirv-fuzz fuzzer pass (#3190) * Start on walkthrough doc. * Finished draft of doc. --- docs/spirv-fuzz.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/spirv-fuzz.md diff --git a/docs/spirv-fuzz.md b/docs/spirv-fuzz.md new file mode 100644 index 0000000000..e5439e3cb9 --- /dev/null +++ b/docs/spirv-fuzz.md @@ -0,0 +1,83 @@ +# Guide to writing a spirv-fuzz fuzzer pass + +Writing a spirv-fuzz fuzzer pass usually requires two main contributions: + +- A *transformation*, capturing a small semantics-preserving change that can be made to a SPIR-V module. This requires adding a protobuf message representing the transformation, and a corresponding class that implements the `Transformation` interface. +- A new *fuzzer pass* class, implementing the `FuzzerPass` interface, that knows how to walk a SPIR-V module and apply the new transformation in a randomized fashion. + +In some cases, more than one kind of transformation is required for a single fuzzer pass, and in some cases the transformations that a new fuzzer pass requires have already been introduced by existing passes. But the most common case is to introduce a transformation and fuzzer pass together. + +As an example, let's consider the `TransformationSetSelectionControl` transformation. In SPIR-V, an `OpSelectionMerge` instruction (which intuitively indicates the start of an `if` or `switch` statement in a function) has a *selection control* mask, that can be one of `None`, `Flatten` or `DontFlatten`. The details of these do not matter much for this little tutorial, but in brief, this parameter provides a hint to the shader compiler as to whether it would be profitable to attempt to flatten a piece of conditional code so that all of its statements are executed in a predicated fashion. + +As the selection control mask is just a hint, changing the value of this mask should have no semantic impact on the module. The `TransformationSelectionControl` transformation specifies a new value for a given selection control mask. + +## Adding a new protobuf message + +Take a look at the `Transformation` message in `spvtoolsfuzz.proto`. This has a `oneof` field that can be any one of the different spirv-fuzz transformations. Observe that one of the options is `TransformationSetSelectionControl`. When adding a transformation you first need to add an option for your transformation to the end of the `oneof` declaration. + +Now look at the `TransformationSetSelectionControl` message. If adding your own transformation you need to add a new message for your transformation, and it should be placed alphabetically with respect to other transformations. + +The fields of `TransformationSetSelectionControl` provide just enough information to (a) determine whether a given example of this transformation is actually applicable, and (b) apply the transformation in the case that it is applicable. The details of the transformation message will vary a lot between transformations. In this case, the message has a `block_id` field, specifying a block that must end with `OpSelectionMerge`, and a `selection_control` field, which is the new value for the selection control mask of the `OpSelectionMerge` instruction. + +## Adding a new transformation class + +If your transformation is called `TransformationSomeThing`, you need to add `transformation_some_thing.h` and `transformation_some_thing.cpp` to `source/fuzz` and the corresponding `CMakeLists.txt` file. So for `TransformationSetSelectionControl` we have `transformation_selection_control.h` and `transformation_selection_control.cpp`, and we will use this as an example to illustrate the expected contents of these files. + +The header file contains the specification of a class, `TransformationSetSelectionControl`, that implements the `Transformation` interface (from `transformation.h`). + +A transformation class should always have a single field, which should be the associated protobuf message; in our case: + +``` + private: + protobufs::TransformationSetSelectionControl message_; +``` + +and two public constructors, one that takes a protobuf message; in our case: + +``` + explicit TransformationSetSelectionControl( + const protobufs::TransformationSetSelectionControl& message); +``` + +and one that takes a parameter for each protobuf message field; in our case: + +``` + TransformationSetSelectionControl(uint32_t block_id); +``` + +The first constructor allows an instance of the class to be created from a corresponding protobuf message. The second should provide the ingredients necessary to populate a protobuf message. + +The class should also override the `IsApplicable`, `Apply` and `ToMessage` methods from `Transformation`. + +See `transformation_set_selection_control.h` for an example. + +The `IsApplicable` method should have a comment in the header file describing the conditions for applicability in simple terms. These conditions should be implemented in the body of this method in the `.cpp` file. + +In the case of `TransformationSetSelectionControl`, `IsApplicable` involves checking that `block_id` is indeed the id of a block that has an `OpSelectoinMerge` instruction, and that `selection_control` is a valid selection mask. + +The `Apply` method should have a comment in the header file summarising the result of applying the transformation. It should be implemented in the `.cpp` file, and you should assume that `IsApplicable` holds whenever `Apply` is invoked. + +## Writing tests for the transformation class + +Whenever you add a transformation class, `TransformationSomeThing`, you should add an associated test file, `transformation_some_thing_test.cpp`, under `test/fuzz`, adding it to the associated `CMakeLists.txt` file. + +For example `test/fuzz/transformation_set_selection_control_test.cpp` contains tests for `TransformationSetSelectionControl`. Your tests should aim to cover one example from each scenario where the transformation is inapplicable, and check that it is indeed deemed inapplicable, and then check that the transformation does the right thing when applied in a few different ways. + +For example, the tests for `TransformationSetSelectionControl` check that a transformation of this kind is inapplicable if the `block_id` field of the transformation is not a block, or does not end in `OpSelectionMerge`, or if the `selection_control` mask has an illegal value. It also checks that applying a sequence of valid transformations to a SPIR-V shader leads to a shader with appropriately modified selection controls. + +## Adding a new fuzzer pass class + +A *fuzzer pass* traverses a SPIR-V module looking for places to apply a certain kind of transformation, and randomly decides at which of these points to actually apply the transformation. It might be necessary to apply other transformations in order to apply a given transformation (for example, if a transformation requires a certain type to be present in the module, said type can be added if not already present via another transformation). + +A fuzzer pass implements the `FuzzerPass` interface, and overrides its `Apply` method. If your fuzzer pass is named `FuzzerPassSomeThing` then it should be represented by `fuzzer_pass_some_thing.h` and `fuzzer_pass_some_thing.cpp`, under `source/fuzz`; these should be added to the associated `CMakeLists.txt` file. + +Have a look at the source filed for `FuzzerPassAdjustSelectionControls`. This pass considers every block that ends with `OpSelectionMerge`. It decides randomly whether to adjust the selection control of this merge instruction via: + +``` +if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) { + continue; +} +``` + +The `GetChanceOfAddingSelectionControl()` method has been added to `FuzzerContext` specifically to support this pass, and returns a percentage between 0 and 100. It returns the `chance_of_adjusting_selection_control_` of `FuzzerContext`, which is randomly initialized to lie with the interval defined by `kChanceOfAdjustingSelectionControl` in `fuzzer_context.cpp`. For any pass you write, you will need to add an analogous `GetChanceOf...` method to `FuzzerContext`, backed by an appropriate field, and you will need to decide on lower and upper bounds for this field and specify these via a `kChanceOf...` constant. From c316fb15fb0ffdcb23b93e824604d31eed46b136 Mon Sep 17 00:00:00 2001 From: Geoff Lang Date: Fri, 21 Feb 2020 17:23:42 -0500 Subject: [PATCH 28/88] Add missing dependencies when generating spvtools_core_tables (#3199) --- BUILD.gn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BUILD.gn b/BUILD.gn index 28dcc3d727..b7cde34220 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -37,6 +37,8 @@ template("spvtools_core_tables") { sources = [ core_json_file, + debuginfo_insts_file, + cldebuginfo100_insts_file, ] outputs = [ core_insts_file, From 8910ea5f1c7bc38f79a8b70b265cd9d1571f4b56 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sun, 23 Feb 2020 18:50:59 -0500 Subject: [PATCH 29/88] Fix Wrange-loop-analysis warnings in SPIRV-Tools. (#3201) --- source/val/validate.cpp | 4 ++-- source/val/validation_state.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 7f4b0dce46..168968da4d 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -142,7 +142,7 @@ spv_result_t ValidateEntryPointNameUnique(ValidationState_t& _, for (const auto other_id : _.entry_points()) { if (other_id == id) continue; const auto other_id_names = CalculateNamesForEntryPoint(_, other_id); - for (const auto other_id_name : other_id_names) { + for (const auto& other_id_name : other_id_names) { if (names.find(other_id_name) != names.end()) { return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id)) << "Entry point name \"" << other_id_name @@ -431,7 +431,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( if (auto error = ValidateBuiltIns(*vstate)) return error; // These checks must be performed after individual opcode checks because // those checks register the limitation checked here. - for (const auto inst : vstate->ordered_instructions()) { + for (const auto& inst : vstate->ordered_instructions()) { if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error; if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error; } diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 51aebbe513..0739148e4c 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -1052,7 +1052,7 @@ void ValidationState_t::ComputeFunctionToEntryPointMapping() { } void ValidationState_t::ComputeRecursiveEntryPoints() { - for (const Function func : functions()) { + for (const Function& func : functions()) { std::stack call_stack; std::set visited; From fb6e3e48d51d1efdc31f016d7e52f528b93235a8 Mon Sep 17 00:00:00 2001 From: Geoff Lang Date: Tue, 25 Feb 2020 00:46:52 -0500 Subject: [PATCH 30/88] Combine extinst-name and extinst-output-base into one arg. (#3200) * Combine the extinst-name and extinst-output-base into one arg. Some build systems such as Android blueprints require that the inputs and outputs of generator scripts are all provided as arguments. These two arguments to generate_language_headers.py are combined to form the output path in the script. This change simply lets the user provide the whole output path as an argument. * Fix typo in build_defs.bzl and update Android.mk --- Android.mk | 3 +-- BUILD.gn | 10 ++++------ build_defs.bzl | 8 ++++---- source/CMakeLists.txt | 6 ++---- utils/generate_language_headers.py | 14 ++++++-------- 5 files changed, 17 insertions(+), 24 deletions(-) diff --git a/Android.mk b/Android.mk index 4fab1ec73d..db4f43bdf4 100644 --- a/Android.mk +++ b/Android.mk @@ -235,9 +235,8 @@ $(1)/$(2).h : \ $(LOCAL_PATH)/utils/generate_language_headers.py \ $(3) @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_language_headers.py \ - --extinst-name=$(2) \ --extinst-grammar=$(3) \ - --extinst-output-base=$(1)/$(2) + --extinst-output-path=$(1)/$(2).h @echo "[$(TARGET_ARCH_ABI)] Generate language specific header for $(2): headers <= grammar" $(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \ : $(1)/$(2).h diff --git a/BUILD.gn b/BUILD.gn index b7cde34220..1337059dee 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -186,21 +186,19 @@ template("spvtools_language_header") { script = "utils/generate_language_headers.py" name = invoker.name - extinst_output_base = "${target_gen_dir}/${name}" + extinst_output_path = "${target_gen_dir}/${name}.h" args = [ - "--extinst-name", - "${name}", "--extinst-grammar", rebase_path(invoker.grammar_file, root_build_dir), - "--extinst-output-base", - rebase_path(extinst_output_base, root_build_dir), + "--extinst-output-path", + rebase_path(extinst_output_path, root_build_dir), ] inputs = [ invoker.grammar_file, ] outputs = [ - "${extinst_output_base}.h", + "${extinst_output_path}", ] } } diff --git a/build_defs.bzl b/build_defs.bzl index 5d913a14c8..15b70c733f 100644 --- a/build_defs.bzl +++ b/build_defs.bzl @@ -167,16 +167,16 @@ def generate_vendor_tables(extension, operand_kind_prefix = ""): def generate_extinst_lang_headers(name, grammar = None): if not grammar: fail("Must specify grammar", "grammar") - fmtargs = [name] + outs = [name + ".h"] + fmtargs = outs native.genrule( name = "gen_extinst_lang_headers_" + name, srcs = [grammar], - outs = [name + ".h"], + outs = outs, cmd = ( "$(location :generate_language_headers) " + - "--extinst-name={0} " + "--extinst-grammar=$< " + - "--extinst-output-base=$(@D)/{0}" + "--extinst-output-path=$(location {0})" ).format(*fmtargs), tools = [":generate_language_headers"], visibility = ["//visibility:private"], diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 4e7e10cb6b..708ca84837 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -126,13 +126,11 @@ macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) endmacro(spvtools_vendor_tables) macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE) - set(OUTBASE ${spirv-tools_BINARY_DIR}/${NAME}) - set(OUT_H ${OUTBASE}.h) + set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h) add_custom_command(OUTPUT ${OUT_H} COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT} - --extinst-name=${NAME} --extinst-grammar=${GRAMMAR_FILE} - --extinst-output-base=${OUTBASE} + --extinst-output-path=${OUT_H} DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE} COMMENT "Generate language specific header for ${NAME}.") add_custom_target(spirv-tools-header-${NAME} DEPENDS ${OUT_H}) diff --git a/utils/generate_language_headers.py b/utils/generate_language_headers.py index 0296163365..83fa99e1f7 100755 --- a/utils/generate_language_headers.py +++ b/utils/generate_language_headers.py @@ -159,27 +159,25 @@ def main(): import argparse parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar') - parser.add_argument('--extinst-name', - type=str, required=True, - help='The name to use in tokens') parser.add_argument('--extinst-grammar', metavar='', type=str, required=True, help='input JSON grammar file for extended instruction set') - parser.add_argument('--extinst-output-base', metavar='', + parser.add_argument('--extinst-output-path', metavar='', type=str, required=True, - help='Basename of the language-specific output file.') + help='Path of the language-specific output file.') args = parser.parse_args() with open(args.extinst_grammar) as json_file: grammar_json = json.loads(json_file.read()) - grammar = ExtInstGrammar(name = args.extinst_name, + grammar_name = os.path.splitext(os.path.basename(args.extinst_output_path))[0] + grammar = ExtInstGrammar(name = grammar_name, copyright = grammar_json['copyright'], instructions = grammar_json['instructions'], operand_kinds = grammar_json['operand_kinds'], version = grammar_json['version'], revision = grammar_json['revision']) - make_path_to_file(args.extinst_output_base) - with open(args.extinst_output_base + '.h', 'w') as f: + make_path_to_file(args.extinst_output_path) + with open(args.extinst_output_path, 'w') as f: f.write(CGenerator().generate(grammar)) From 70f888131ede3d267cb19a413c9fef3fc51ccc22 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Tue, 25 Feb 2020 16:47:03 -0500 Subject: [PATCH 31/88] Add validation rules for OpenCL.DebugInfo.100 extension (#3133) Add validation rules for DebugCompilationUnit, DebugSource, DebugTypeBasic, DebugTypeVector, DebugTypeArray, DebugTypedef, DebugTypeFunction, DebugTypeEnum, DebugTypeComposite, DebugTypeMember, DebugTypeInheritance, DebugFunction, DebugFunctionDeclaration, DebugLexicalBlock, DebugScope, DebugLocalVariable, DebugDeclare, DebugExpression. --- source/val/validate_extensions.cpp | 453 +++++++- source/val/validate_id.cpp | 8 +- test/val/val_ext_inst_test.cpp | 1663 +++++++++++++++++++++++++++- 3 files changed, 2110 insertions(+), 14 deletions(-) diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index 070cc4c6ab..1e311c19d0 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -14,12 +14,11 @@ // Validates correctness of extension SPIR-V instructions. -#include "source/val/validate.h" - #include #include #include +#include "OpenCLDebugInfo100.h" #include "source/diagnostic.h" #include "source/enum_string_mapping.h" #include "source/extensions.h" @@ -28,6 +27,7 @@ #include "source/opcode.h" #include "source/spirv_target_env.h" #include "source/val/instruction.h" +#include "source/val/validate.h" #include "source/val/validation_state.h" namespace spvtools { @@ -42,6 +42,144 @@ uint32_t GetSizeTBitWidth(const ValidationState_t& _) { return 0; } +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an instruction with |expected_opcode|. +spv_result_t ValidateOperandForDebugInfo( + ValidationState_t& _, const std::string& operand_name, + SpvOp expected_opcode, const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + auto* operand = _.FindDef(inst->word(word_index)); + if (operand->opcode() != expected_opcode) { + spv_opcode_desc desc = nullptr; + if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS || + !desc) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << operand_name << " is invalid"; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << operand_name << " must be a result id of " + << "Op" << desc->name; + } + return SPV_SUCCESS; +} + +#define CHECK_OPERAND(NAME, opcode, index) \ + do { \ + auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \ + ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } while (0) + +// True if the operand of a debug info instruction |inst| at |word_index| +// satisifies |expectation| that is given as a function. Otherwise, +// returns false. +bool DoesDebugInfoOperandMatchExpectation( + const ValidationState_t& _, + const std::function& expectation, + const Instruction* inst, uint32_t word_index) { + auto* debug_inst = _.FindDef(inst->word(word_index)); + if (debug_inst->opcode() != SpvOpExtInst || + debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) { + return false; + } + return true; +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an debug info instruction whose debug instruction type +// is |expected_debug_inst|. +spv_result_t ValidateDebugInfoOperand( + ValidationState_t& _, const std::string& debug_inst_name, + OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst, + uint32_t word_index, const std::function& ext_inst_name) { + std::function expectation = + [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == expected_debug_inst; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + spv_ext_inst_desc desc = nullptr; + _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + expected_debug_inst, &desc); + if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + expected_debug_inst, &desc) != SPV_SUCCESS || + !desc) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name << " is invalid"; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name << " must be a result id of " + << desc->name; +} + +#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index) \ + do { \ + auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \ + ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } while (0) + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an debug info instruction with DebugTypeBasic. +spv_result_t ValidateOperandBaseType( + ValidationState_t& _, const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + return ValidateDebugInfoOperand(_, "Base Type", + OpenCLDebugInfo100DebugTypeBasic, inst, + word_index, ext_inst_name); +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of a debug lexical scope instruction which is one of +// DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or +// DebugTypeComposite. +spv_result_t ValidateOperandLexicalScope( + ValidationState_t& _, const std::string& debug_inst_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + std::function expectation = + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit || + dbg_inst == OpenCLDebugInfo100DebugFunction || + dbg_inst == OpenCLDebugInfo100DebugLexicalBlock || + dbg_inst == OpenCLDebugInfo100DebugTypeComposite; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name + << " must be a result id of a lexical scope"; +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of a debug type instruction (See DebugTypeXXX in +// "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec. +spv_result_t ValidateOperandDebugType( + ValidationState_t& _, const std::string& debug_inst_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + std::function expectation = + [](OpenCLDebugInfo100Instructions dbg_inst) { + return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst && + dbg_inst <= OpenCLDebugInfo100DebugTypePtrToMember; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name + << " is not a valid debug type"; +} + } // anonymous namespace spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { @@ -2028,6 +2166,317 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { break; } } + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + if (!_.IsVoidType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected result type must be a result id of " + << "OpTypeVoid"; + } + + auto num_words = inst->words().size(); + + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case OpenCLDebugInfo100DebugInfoNone: + case OpenCLDebugInfo100DebugNoScope: + case OpenCLDebugInfo100DebugOperation: + // The binary parser validates the opcode for DebugInfoNone, + // DebugNoScope, DebugOperation, and the literal values don't need + // further checks. + break; + case OpenCLDebugInfo100DebugCompilationUnit: { + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + break; + } + case OpenCLDebugInfo100DebugSource: { + CHECK_OPERAND("File", SpvOpString, 5); + if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6); + break; + } + case OpenCLDebugInfo100DebugTypeBasic: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_OPERAND("Size", SpvOpConstant, 6); + // "Encoding" param is already validated by the binary parsing stage. + break; + } + case OpenCLDebugInfo100DebugTypePointer: + case OpenCLDebugInfo100DebugTypeQualifier: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + break; + } + case OpenCLDebugInfo100DebugTypeVector: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + + uint32_t component_count = inst->word(6); + if (!component_count || component_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case OpenCLDebugInfo100DebugTypeArray: { + auto validate_base_type = + ValidateOperandDebugType(_, "Base Type", inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + for (uint32_t i = 6; i < num_words; ++i) { + CHECK_OPERAND("Component Count", SpvOpConstant, i); + auto* component_count = _.FindDef(inst->word(i)); + if (!_.IsIntScalarType(component_count->type_id()) || + !component_count->word(3)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be positive " + << "integer"; + } + } + break; + } + case OpenCLDebugInfo100DebugTypedef: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_base_type = + ValidateOperandBaseType(_, inst, 6, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case OpenCLDebugInfo100DebugTypeFunction: { + auto* return_type = _.FindDef(inst->word(6)); + if (return_type->opcode() != SpvOpTypeVoid) { + auto validate_return = ValidateOperandDebugType( + _, "Return Type", inst, 6, ext_inst_name); + if (validate_return != SPV_SUCCESS) return validate_return; + } + for (uint32_t word_index = 7; word_index < num_words; ++word_index) { + auto validate_param = ValidateOperandDebugType( + _, "Parameter Types", inst, word_index, ext_inst_name); + if (validate_param != SPV_SUCCESS) return validate_param; + } + break; + } + case OpenCLDebugInfo100DebugTypeEnum: { + CHECK_OPERAND("Name", SpvOpString, 5); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugInfoNone; + }, + inst, 6)) { + auto validate_underlying_type = ValidateOperandDebugType( + _, "Underlying Types", inst, 6, ext_inst_name); + if (validate_underlying_type != SPV_SUCCESS) + return validate_underlying_type; + } + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Size", SpvOpConstant, 11); + auto* size = _.FindDef(inst->word(11)); + if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Size is a " + << "positive integer"; + } + for (uint32_t word_index = 13; word_index + 1 < num_words; + word_index += 2) { + CHECK_OPERAND("Value", SpvOpConstant, word_index); + CHECK_OPERAND("Name", SpvOpString, word_index + 1); + } + break; + } + case OpenCLDebugInfo100DebugTypeComposite: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + CHECK_OPERAND("Size", SpvOpConstant, 12); + for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugTypeMember || + dbg_inst == OpenCLDebugInfo100DebugFunction || + dbg_inst == OpenCLDebugInfo100DebugTypeInheritance; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Members " + << "must be DebugTypeMember, DebugFunction, or " + "DebugTypeInheritance"; + } + } + break; + } + case OpenCLDebugInfo100DebugTypeMember: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10); + CHECK_OPERAND("Offset", SpvOpConstant, 11); + CHECK_OPERAND("Size", SpvOpConstant, 12); + if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14); + break; + } + case OpenCLDebugInfo100DebugTypeInheritance: { + CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5); + auto* debug_inst = _.FindDef(inst->word(5)); + auto composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Child must be class or struct debug type"; + } + CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6); + debug_inst = _.FindDef(inst->word(6)); + composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parent must be class or struct debug " + "type"; + } + CHECK_OPERAND("Offset", SpvOpConstant, 7); + CHECK_OPERAND("Size", SpvOpConstant, 8); + break; + } + case OpenCLDebugInfo100DebugFunction: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + // TODO: The current OpenCL.100.DebugInfo spec says "Function + // is an OpFunction which is described by this instruction.". + // However, the function definition can be opted-out e.g., + // inlining. We assume that Function operand can be a + // DebugInfoNone, but we must discuss it and update the spec. + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugInfoNone; + }, + inst, 14)) { + CHECK_OPERAND("Function", SpvOpFunction, 14); + } + if (num_words == 16) { + CHECK_DEBUG_OPERAND("Declaration", + OpenCLDebugInfo100DebugFunctionDeclaration, 15); + } + break; + } + case OpenCLDebugInfo100DebugFunctionDeclaration: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + break; + } + case OpenCLDebugInfo100DebugLexicalBlock: { + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9); + break; + } + case OpenCLDebugInfo100DebugScope: { + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We are + // still in spec discussion about what must be "Scope" operand of + // DebugScope. Update this code if the conclusion is different. + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 7) { + CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt, + 6); + } + break; + } + case OpenCLDebugInfo100DebugLocalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case OpenCLDebugInfo100DebugDeclare: { + CHECK_DEBUG_OPERAND("Local Variable", + OpenCLDebugInfo100DebugLocalVariable, 5); + + // TODO: We must discuss DebugDeclare.Variable of OpenCL.100.DebugInfo. + // Currently, it says "Variable must be an id of OpVariable instruction + // which defines the local variable.", but we want to allow + // OpFunctionParameter as well. + auto* operand = _.FindDef(inst->word(6)); + if (operand->opcode() != SpvOpVariable && + operand->opcode() != SpvOpFunctionParameter) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpFunctionParameter"; + } + + CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); + break; + } + case OpenCLDebugInfo100DebugExpression: { + for (uint32_t word_index = 5; word_index < num_words; ++word_index) { + CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation, + word_index); + } + break; + } + + // TODO: Add validation rules for remaining cases as well. + case OpenCLDebugInfo100DebugTypePtrToMember: + case OpenCLDebugInfo100DebugTypeTemplate: + case OpenCLDebugInfo100DebugTypeTemplateParameter: + case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter: + case OpenCLDebugInfo100DebugTypeTemplateParameterPack: + case OpenCLDebugInfo100DebugGlobalVariable: + case OpenCLDebugInfo100DebugLexicalBlockDiscriminator: + case OpenCLDebugInfo100DebugInlinedAt: + case OpenCLDebugInfo100DebugInlinedVariable: + case OpenCLDebugInfo100DebugValue: + case OpenCLDebugInfo100DebugMacroDef: + case OpenCLDebugInfo100DebugMacroUndef: + case OpenCLDebugInfo100DebugImportedEntity: + break; + case OpenCLDebugInfo100InstructionsMax: + assert(0); + break; + } } return SPV_SUCCESS; diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp index c171d310d6..e1a775a852 100644 --- a/source/val/validate_id.cpp +++ b/source/val/validate_id.cpp @@ -171,8 +171,8 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) { const auto opcode = inst->opcode(); if (spvOpcodeGeneratesType(def->opcode()) && !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) && - !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && - opcode != SpvOpFunction && + !inst->IsDebugInfo() && !inst->IsNonSemantic() && + !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction && opcode != SpvOpCooperativeMatrixLengthNV && !(opcode == SpvOpSpecConstantOp && inst->word(3) == SpvOpCooperativeMatrixLengthNV)) { @@ -180,8 +180,8 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) { << "Operand " << _.getIdName(operand_word) << " cannot be a type"; } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) && - !spvOpcodeIsDebug(opcode) && !inst->IsNonSemantic() && - !spvOpcodeIsDecoration(opcode) && + !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() && + !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi && opcode != SpvOpExtInst && opcode != SpvOpExtInstImport && opcode != SpvOpSelectionMerge && diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index 01df796b10..aa739897bc 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -36,6 +36,26 @@ using ValidateExtInst = spvtest::ValidateBase; using ValidateOldDebugInfo = spvtest::ValidateBase; using ValidateOpenCL100DebugInfo = spvtest::ValidateBase; using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeInheritance = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugDeclare = + spvtest::ValidateBase>; using ValidateGlslStd450SqrtLike = spvtest::ValidateBase; using ValidateGlslStd450FMinLike = spvtest::ValidateBase; using ValidateGlslStd450FClampLike = spvtest::ValidateBase; @@ -798,6 +818,37 @@ TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%void_info = OpExtInst %void %DbgExt DebugTypeBasic %void_name %u32_0 Unspecified +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void_info %void_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) { const std::string src = R"( %src = OpString "simple.hlsl" @@ -919,17 +970,1613 @@ main() {} HasSubstr("forward referenced IDs have not been defined")); } -TEST_P(ValidateGlslStd450SqrtLike, Success) { - const std::string ext_inst_name = GetParam(); - std::ostringstream ss; - ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0\n"; - ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name - << " %f32vec2_01\n"; - ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name << " %f64_0\n"; - CompileSuccessfully(GenerateShaderCode(ss.str())); +TEST_F(ValidateOpenCL100DebugInfo, DebugInstructionWrongResultType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected result type must be a result id of " + "OpTypeVoid")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Source must be a result id of " + "DebugSource")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailFile) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand File must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailSource) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Text must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceNoText) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Name must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Size must be a result id of " + "OpConstant")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug " + "type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Component Count must be a result id " + "of OpConstant")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive integer")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive integer")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)", + "Name"), + std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)", + "Base Type"), + std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)", + "Source"), + std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info +%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info +%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Return Type is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Parameter Types is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Underlying Types"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)", + "Parent"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)", + "Value"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)", + "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; + ss << param.first; + ss << R"( +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + " must be ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Linkage Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)", + "Members"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float pos : SV_POSITION; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + if (!param.second.empty()) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); + } +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + ""), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)", + "Offset"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT {}; +struct foo : VS_OUTPUT {}; +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%foo_name = OpString "foo" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be a result id of"), + std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)", + "Parent must be a result id of"), + std::make_pair( + R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be class or struct debug type"), + std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)", + "Parent must be class or struct debug type"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)", + "Offset"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); +TEST_P(ValidateGlslStd450SqrtLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01\n"; + ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name << " %f64_0\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)", + "Linkage Name"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)", + "Function"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)", + "Declaration"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, + ValidateOpenCL100DebugInfoDebugFunctionDeclaration, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)", + "Linkage Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"), + std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"), + std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) { + CompileSuccessfully(R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_COLOR + %4 = OpString "test.hlsl" + OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %11 = OpString "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %14 = OpString "float" + %17 = OpString "src.main" + %20 = OpString "foo" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpName %param_var_foo "param.var.foo" + OpName %src_main "src.main" + OpName %foo "foo" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %29 = OpTypeFunction %void %_ptr_Function_float + OpLine %4 1 21 +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %10 = OpExtInst %void %1 DebugExpression + %12 = OpExtInst %void %1 DebugSource %4 %11 + %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL + %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float + %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15 + %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main + %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18 + OpLine %4 1 1 + %main = OpFunction %void None %23 + %24 = OpLabel + OpLine %4 1 17 +%param_var_foo = OpVariable %_ptr_Function_float Function + %27 = OpLoad %float %in_var_COLOR + OpLine %4 1 1 + %28 = OpFunctionCall %void %src_main %param_var_foo + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %29 + OpLine %4 1 17 + %foo = OpFunctionParameter %_ptr_Function_float + %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 + %bb_entry = OpLabel + OpLine %4 1 29 + OpReturn + OpFunctionEnd +)"); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), + std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) { + const std::string dbg_inst_header = R"( +%op0 = OpExtInst %void %DbgExt DebugOperation Deref +%op1 = OpExtInst %void %DbgExt DebugOperation Plus +%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) { + const std::string dbg_inst_header = R"( +%op = OpExtInst %void %DbgExt DebugOperation Deref +%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Operation must be a result id of DebugOperation")); +} + TEST_P(ValidateGlslStd450SqrtLike, IntResultType) { const std::string ext_inst_name = GetParam(); const std::string body = From 661e79eec807849346f2bb90c0db05a56347dccb Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Fri, 28 Feb 2020 16:42:50 -0500 Subject: [PATCH 32/88] Adding WebGPU specific Workgroup scope rule (#3204) Fixes #3075 --- source/val/validate_scopes.cpp | 30 +++++++++++++ test/val/val_barriers_test.cpp | 77 +++++++++++++++++++++++++++------- 2 files changed, 91 insertions(+), 16 deletions(-) diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index 320d828218..473d6e840c 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp @@ -143,6 +143,20 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _, << spvOpcodeString(opcode) << ": in WebGPU environment Execution Scope is limited to " << "Workgroup"; + } else { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelGLCompute) { + if (message) { + *message = + ": in WebGPU environment, Workgroup Execution Scope is " + "limited to GLCompute execution model"; + } + return false; + } + return true; + }); } } @@ -261,6 +275,22 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, } break; } + + if (value == SpvScopeWorkgroup) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelGLCompute) { + if (message) { + *message = + ": in WebGPU environment, Workgroup Memory Scope is " + "limited to GLCompute execution model"; + } + return false; + } + return true; + }); + } } // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp index ae166d9547..fa2b153664 100644 --- a/test/val/val_barriers_test.cpp +++ b/test/val/val_barriers_test.cpp @@ -120,7 +120,7 @@ OpCapability Int64 execution_model, memory_model); } -std::string GenerateWebGPUShaderCode( +std::string GenerateWebGPUComputeShaderCode( const std::string& body, const std::string& capabilities_and_extensions = "", const std::string& execution_model = "GLCompute") { @@ -138,6 +138,24 @@ OpExtension "SPV_KHR_vulkan_memory_model" "", execution_model, memory_model); } +std::string GenerateWebGPUVertexShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Vertex") { + const std::string vulkan_memory_capability = R"( +OpCapability VulkanMemoryModelKHR +)"; + const std::string vulkan_memory_extension = R"( +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + const std::string memory_model = "OpMemoryModel Logical VulkanKHR"; + return GenerateShaderCodeImpl(body, + vulkan_memory_capability + + capabilities_and_extensions + + vulkan_memory_extension, + "", execution_model, memory_model); +} + std::string GenerateKernelCode( const std::string& body, const std::string& capabilities_and_extensions = "") { @@ -257,7 +275,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireReleaseSuccess) { OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } @@ -266,7 +284,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPURelaxedFailure) { OpControlBarrier %workgroup %workgroup %workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For WebGPU, AcquireRelease must be set for Memory " @@ -278,7 +296,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPUMissingWorkgroupFailure) { OpControlBarrier %workgroup %workgroup %acquire_release )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For WebGPU, WorkgroupMemory must be set for Memory " @@ -290,7 +308,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPUUniformFailure) { OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT( getDiagnosticString(), @@ -303,7 +321,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPUReleaseFailure) { OpControlBarrier %workgroup %workgroup %release_uniform_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For WebGPU, AcquireRelease must be set for Memory " @@ -421,7 +439,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeDeviceBad) { OpControlBarrier %device %workgroup %none )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: in WebGPU environment Execution Scope " @@ -433,13 +451,27 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeSubgroupBad) { OpControlBarrier %subgroup %workgroup %none )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: in WebGPU environment Execution Scope " "is limited to Workgroup")); } +TEST_F(ValidateBarriers, + OpControlBarrierWebGPUExecutionScopeWorkgroupNonComputeBad) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Workgroup Execution Scope is limited to GLCompute execution model")); +} + TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %none @@ -479,7 +511,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWebGPUMemoryScopeNonWorkgroup) { OpControlBarrier %workgroup %subgroup %acquire_release_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: in WebGPU environment Memory Scope is " @@ -710,7 +742,7 @@ TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUImageMemorySuccess) { OpMemoryBarrier %workgroup %image_memory )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } @@ -719,7 +751,7 @@ TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUDeviceFailure) { OpMemoryBarrier %subgroup %image_memory )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("in WebGPU environment Memory Scope is limited to " @@ -731,7 +763,7 @@ TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireReleaseFailure) { OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("ImageMemory must be set for Memory Semantics of " @@ -743,7 +775,7 @@ TEST_F(ValidateBarriers, OpMemoryBarrierWebGPURelaxedFailure) { OpMemoryBarrier %workgroup %uniform_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("ImageMemory must be set for Memory Semantics of " @@ -755,7 +787,7 @@ TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireFailure) { OpMemoryBarrier %workgroup %acquire_uniform_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("ImageMemory must be set for Memory Semantics of " @@ -767,7 +799,7 @@ TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUReleaseFailure) { OpMemoryBarrier %workgroup %release_uniform_workgroup )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("ImageMemory must be set for Memory Semantics of " @@ -779,13 +811,26 @@ TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUUniformFailure) { OpMemoryBarrier %workgroup %uniform_image_memory )"; - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("only ImageMemory may be set for Memory Semantics of " "OpMemoryBarrier")); } +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUWorkgroupNonComputeFailure) { + const std::string body = R"( +OpMemoryBarrier %workgroup %image_memory +)"; + + CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Workgroup Memory Scope is limited to GLCompute execution model")); +} + TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) { const std::string body = R"( OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup From e1688b60caf77e7efd9e440e57cca429ca7c5a1e Mon Sep 17 00:00:00 2001 From: David Neto Date: Fri, 28 Feb 2020 16:50:06 -0500 Subject: [PATCH 33/88] Avoid use of Python distutils.dir_util (#3203) Required for compatibility with (some?) Python3 installations. --- utils/generate_registry_tables.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/utils/generate_registry_tables.py b/utils/generate_registry_tables.py index e662ba99bf..28152ef3eb 100755 --- a/utils/generate_registry_tables.py +++ b/utils/generate_registry_tables.py @@ -14,11 +14,30 @@ # limitations under the License. """Generates the vendor tool table from the SPIR-V XML registry.""" -import distutils.dir_util +import errno import os.path import xml.etree.ElementTree +def mkdir_p(directory): + """Make the directory, and all its ancestors as required. Any of the + directories are allowed to already exist. + This is compatible with Python down to 3.0. + """ + + if directory == "": + # We're being asked to make the current directory. + return + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + + def generate_vendor_table(registry): """Returns a list of C style initializers for the registered vendors and their tools. @@ -62,7 +81,7 @@ def main(): with open(args.xml) as xml_in: registry = xml.etree.ElementTree.fromstring(xml_in.read()) - distutils.dir_util.mkpath(os.path.dirname(args.generator_output)) + mkdir_p(os.path.dirname(args.generator_output)) with open(args.generator_output, 'w') as f: f.write(generate_vendor_table(registry)) From a6d3a2dd41d4efcafaefb4821ad491262be6b8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Perez=20Maselco?= Date: Wed, 4 Mar 2020 03:56:38 -0300 Subject: [PATCH 34/88] Refactor FuzzerPass::ApplyTransformation code duplication. (#3206) --- .../fuzzer_pass_add_no_contraction_decorations.cpp | 7 +------ .../fuzz/fuzzer_pass_adjust_function_controls.cpp | 5 +---- source/fuzz/fuzzer_pass_adjust_loop_controls.cpp | 6 +----- .../fuzzer_pass_adjust_memory_operands_masks.cpp | 7 +------ .../fuzz/fuzzer_pass_adjust_selection_controls.cpp | 6 +----- source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 14 ++------------ 6 files changed, 7 insertions(+), 38 deletions(-) diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp index ead8c5cd55..82fb539982 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -44,12 +44,7 @@ void FuzzerPassAddNoContractionDecorations::Apply() { ->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(); + ApplyTransformation(transformation); } } } diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp index 2a11988f47..fe229bca49 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -61,10 +61,7 @@ void FuzzerPassAdjustFunctionControls::Apply() { // 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(); + ApplyTransformation(transformation); } } } diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp index ac2408aef9..c9843d0a25 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -107,11 +107,7 @@ void FuzzerPassAdjustLoopControls::Apply() { // 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(); + ApplyTransformation(transformation); } } } diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp index a9d4b3243b..2d3d676535 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -97,12 +97,7 @@ void FuzzerPassAdjustMemoryOperandsMasks::Apply() { TransformationSetMemoryOperandsMask transformation( MakeInstructionDescriptor(block, inst_it), new_mask, mask_index); - assert( - transformation.IsApplicable(GetIRContext(), *GetFactManager()) && - "Transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetFactManager()); - *GetTransformations()->add_transformation() = - transformation.ToMessage(); + ApplyTransformation(transformation); } } } diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp index 22654f242f..397dfedb07 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -62,11 +62,7 @@ void FuzzerPassAdjustSelectionControls::Apply() { // 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(); + ApplyTransformation(transformation); } } } diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index e932017df1..1575fa99e3 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -117,13 +117,7 @@ void FuzzerPassApplyIdSynonyms::Apply() { instruction_to_insert_before, id_with_which_to_replace_use, synonym_to_try->object(), fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())); - assert(composite_extract_transformation.IsApplicable( - GetIRContext(), *GetFactManager()) && - "Transformation should be applicable by construction."); - composite_extract_transformation.Apply(GetIRContext(), - GetFactManager()); - *GetTransformations()->add_transformation() = - composite_extract_transformation.ToMessage(); + ApplyTransformation(composite_extract_transformation); } TransformationReplaceIdWithSynonym replace_id_transformation( @@ -132,11 +126,7 @@ void FuzzerPassApplyIdSynonyms::Apply() { id_with_which_to_replace_use); // The transformation should be applicable by construction. - assert(replace_id_transformation.IsApplicable(GetIRContext(), - *GetFactManager())); - replace_id_transformation.Apply(GetIRContext(), GetFactManager()); - *GetTransformations()->add_transformation() = - replace_id_transformation.ToMessage(); + ApplyTransformation(replace_id_transformation); break; } } From 044ecc0b2c65e840270fe57b9d72e0fa0c31b1e7 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 4 Mar 2020 14:54:08 +0000 Subject: [PATCH 35/88] spirv-fuzz: Fuzzer pass to add equation instructions (#3202) This introduces a new fuzzer pass to add instructions to the module that define equations, and support in the fact manager for recording equation facts and deducing synonym facts from equation facts. Initially the only equations that are supported involve OpIAdd, OpISub, OpSNegate and OpLogicalNot, but there is scope for adding support for equations over various other operators. --- source/fuzz/CMakeLists.txt | 4 + source/fuzz/equivalence_relation.h | 37 +- source/fuzz/fact_manager.cpp | 481 +++++++++++++++--- source/fuzz/fact_manager.h | 15 +- source/fuzz/fuzzer.cpp | 4 + source/fuzz/fuzzer_context.cpp | 4 + source/fuzz/fuzzer_context.h | 4 + .../fuzzer_pass_add_equation_instructions.cpp | 235 +++++++++ .../fuzzer_pass_add_equation_instructions.h | 67 +++ source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 42 +- source/fuzz/protobufs/spvtoolsfuzz.proto | 69 ++- source/fuzz/transformation.cpp | 4 + .../transformation_equation_instruction.cpp | 186 +++++++ .../transformation_equation_instruction.h | 76 +++ test/fuzz/CMakeLists.txt | 1 + test/fuzz/fact_manager_test.cpp | 200 ++++++++ ...ansformation_equation_instruction_test.cpp | 462 +++++++++++++++++ 17 files changed, 1775 insertions(+), 116 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_add_equation_instructions.cpp create mode 100644 source/fuzz/fuzzer_pass_add_equation_instructions.h create mode 100644 source/fuzz/transformation_equation_instruction.cpp create mode 100644 source/fuzz/transformation_equation_instruction.h create mode 100644 test/fuzz/transformation_equation_instruction_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 4d5feea574..5b2afe4f17 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -42,6 +42,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_blocks.h fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_dead_continues.h + fuzzer_pass_add_equation_instructions.h fuzzer_pass_add_function_calls.h fuzzer_pass_add_global_variables.h fuzzer_pass_add_loads.h @@ -96,6 +97,7 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_construct.h transformation_composite_extract.h transformation_copy_object.h + transformation_equation_instruction.h transformation_function_call.h transformation_load.h transformation_merge_blocks.h @@ -126,6 +128,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_dead_blocks.cpp fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_dead_continues.cpp + fuzzer_pass_add_equation_instructions.cpp fuzzer_pass_add_function_calls.cpp fuzzer_pass_add_global_variables.cpp fuzzer_pass_add_loads.cpp @@ -179,6 +182,7 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_construct.cpp transformation_composite_extract.cpp transformation_copy_object.cpp + transformation_equation_instruction.cpp transformation_function_call.cpp transformation_load.cpp transformation_merge_blocks.cpp diff --git a/source/fuzz/equivalence_relation.h b/source/fuzz/equivalence_relation.h index 046536f80e..7bb8b66803 100644 --- a/source/fuzz/equivalence_relation.h +++ b/source/fuzz/equivalence_relation.h @@ -75,18 +75,8 @@ class EquivalenceRelation { // Register each value if necessary. for (auto value : {value1, value2}) { if (!Exists(value)) { - // Register the value in the equivalence relation. This relies on - // T having a copy constructor. - auto unique_pointer_to_value = MakeUnique(value); - auto pointer_to_value = unique_pointer_to_value.get(); - owned_values_.push_back(std::move(unique_pointer_to_value)); - value_set_.insert(pointer_to_value); - - // Initially say that the value is its own parent and that it has no - // children. - assert(pointer_to_value && "Representatives should never be null."); - parent_[pointer_to_value] = pointer_to_value; - children_[pointer_to_value] = std::vector(); + // Register the value in the equivalence relation. + Register(value); } } @@ -112,6 +102,27 @@ class EquivalenceRelation { } } + // Requires that |value| is not known to the equivalence relation. Registers + // it in its own equivalence class and returns a pointer to the equivalence + // class representative. + const T* Register(T& value) { + assert(!Exists(value)); + + // This relies on T having a copy constructor. + auto unique_pointer_to_value = MakeUnique(value); + auto pointer_to_value = unique_pointer_to_value.get(); + owned_values_.push_back(std::move(unique_pointer_to_value)); + value_set_.insert(pointer_to_value); + + // Initially say that the value is its own parent and that it has no + // children. + assert(pointer_to_value && "Representatives should never be null."); + parent_[pointer_to_value] = pointer_to_value; + children_[pointer_to_value] = std::vector(); + + return pointer_to_value; + } + // Returns exactly one representative per equivalence class. std::vector GetEquivalenceClassRepresentatives() const { std::vector result; @@ -168,7 +179,6 @@ class EquivalenceRelation { return value_set_.find(&value) != value_set_.end(); } - private: // Returns the representative of the equivalence class of |value|, which must // already be known to the equivalence relation. This is the 'Find' operation // in a classic union-find data structure. @@ -207,6 +217,7 @@ class EquivalenceRelation { return result; } + private: // Maps every value to a parent. The representative of an equivalence class // is its own parent. A value's representative can be found by walking its // chain of ancestors. diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp index 486e8f546f..31d3b947f8 100644 --- a/source/fuzz/fact_manager.cpp +++ b/source/fuzz/fact_manager.cpp @@ -28,24 +28,13 @@ namespace fuzz { namespace { -std::string ToString(const protobufs::Fact& fact) { - assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact && - "Right now this is the only fact we know how to stringify."); +std::string ToString(const protobufs::FactConstantUniform& fact) { std::stringstream stream; - stream << "(" - << fact.constant_uniform_fact() - .uniform_buffer_element_descriptor() - .descriptor_set() - << ", " - << fact.constant_uniform_fact() - .uniform_buffer_element_descriptor() - .binding() - << ")["; + stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set() + << ", " << fact.uniform_buffer_element_descriptor().binding() << ")["; bool first = true; - for (auto index : fact.constant_uniform_fact() - .uniform_buffer_element_descriptor() - .index()) { + for (auto index : fact.uniform_buffer_element_descriptor().index()) { if (first) { first = false; } else { @@ -57,7 +46,7 @@ std::string ToString(const protobufs::Fact& fact) { stream << "] == ["; first = true; - for (auto constant_word : fact.constant_uniform_fact().constant_word()) { + for (auto constant_word : fact.constant_word()) { if (first) { first = false; } else { @@ -70,6 +59,36 @@ std::string ToString(const protobufs::Fact& fact) { return stream.str(); } +std::string ToString(const protobufs::FactDataSynonym& fact) { + std::stringstream stream; + stream << fact.data1() << " = " << fact.data2(); + return stream.str(); +} + +std::string ToString(const protobufs::FactIdEquation& fact) { + std::stringstream stream; + stream << fact.lhs_id(); + stream << " " << static_cast(fact.opcode()); + for (auto rhs_id : fact.rhs_id()) { + stream << " " << rhs_id; + } + return stream.str(); +} + +std::string ToString(const protobufs::Fact& fact) { + switch (fact.fact_case()) { + case protobufs::Fact::kConstantUniformFact: + return ToString(fact.constant_uniform_fact()); + case protobufs::Fact::kDataSynonymFact: + return ToString(fact.data_synonym_fact()); + case protobufs::Fact::kIdEquationFact: + return ToString(fact.id_equation_fact()); + default: + assert(false && "Stringification not supported for this fact."); + return ""; + } +} + } // namespace //======================= @@ -331,15 +350,70 @@ FactManager::ConstantUniformFacts::GetConstantUniformFactsAndTypes() const { //============================== //============================== -// Data synonym facts +// Data synonym and id equation facts + +// This helper struct represents the right hand side of an equation as an +// operator applied to a number of data descriptor operands. +struct Operation { + SpvOp opcode; + std::vector operands; +}; + +// Hashing for operations, to allow deterministic unordered sets. +struct OperationHash { + size_t operator()(const Operation& operation) const { + std::u32string hash; + hash.push_back(operation.opcode); + for (auto operand : operation.operands) { + hash.push_back(static_cast(DataDescriptorHash()(operand))); + } + return std::hash()(hash); + } +}; + +// Equality for operations, to allow deterministic unordered sets. +struct OperationEquals { + bool operator()(const Operation& first, const Operation& second) const { + // Equal operations require... + // + // Equal opcodes. + if (first.opcode != second.opcode) { + return false; + } + // Matching operand counds. + if (first.operands.size() != second.operands.size()) { + return false; + } + // Equal operands. + for (uint32_t i = 0; i < first.operands.size(); i++) { + if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) { + return false; + } + } + return true; + } +}; + +// A helper, for debugging, to represent an operation as a string. +std::string ToString(const Operation& operation) { + std::stringstream stream; + stream << operation.opcode; + for (auto operand : operation.operands) { + stream << " " << *operand; + } + return stream.str(); +} // The purpose of this class is to group the fields and data used to represent -// facts about data synonyms. -class FactManager::DataSynonymFacts { +// facts about data synonyms and id equations. +class FactManager::DataSynonymAndIdEquationFacts { public: // See method in FactManager which delegates to this method. void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context); + // See method in FactManager which delegates to this method. + void AddFact(const protobufs::FactIdEquation& fact, opt::IRContext* context); + // See method in FactManager which delegates to this method. std::vector GetSynonymsForDataDescriptor( const protobufs::DataDescriptor& data_descriptor, @@ -355,11 +429,12 @@ class FactManager::DataSynonymFacts { opt::IRContext* context) const; private: - // Adds |fact| to the set of managed facts, and recurses into sub-components - // of the data descriptors referenced in |fact|, if they are composites, to + // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses + // into sub-components of the data descriptors, if they are composites, to // record that their components are pairwise-synonymous. - void AddFactRecursive(const protobufs::FactDataSynonym& fact, - opt::IRContext* context); + void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2, + opt::IRContext* context); // Inspects all known facts and adds corollary facts; e.g. if we know that // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record @@ -371,11 +446,21 @@ class FactManager::DataSynonymFacts { void ComputeClosureOfFacts(opt::IRContext* context) const; // Returns true if and only if |dd1| and |dd2| are valid data descriptors - // whose associated data have the same type. + // whose associated data have the same type (modulo integer signedness). bool DataDescriptorsAreWellFormedAndComparable( opt::IRContext* context, const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2) const; + // Requires that |lhs_dd| and every element of |rhs_dds| is present in the + // |synonymous_| equivalence relation and is its own representative. Records + // the fact that the equation "|lhs_dd| |opcode| |rhs_dds|" holds, and adds + // any corollaries, in the form of data synonym or equation facts, that + // follow from this and other known facts. + void AddEquationFactRecursive( + const protobufs::DataDescriptor& lhs_dd, SpvOp opcode, + const std::vector& rhs_dds, + opt::IRContext* context); + // The data descriptors that are known to be synonymous with one another are // captured by this equivalence relation. // @@ -396,31 +481,243 @@ class FactManager::DataSynonymFacts { // whether a new fact has been added since the last time such a computation // was performed. // - // It is mutable so faciliate having const methods, that provide answers to + // It is mutable to facilitate having const methods, that provide answers to // questions about data synonym facts, triggering closure computation on // demand. - mutable bool closure_computation_required = false; + mutable bool closure_computation_required_ = false; + + // Represents a set of equations on data descriptors as a map indexed by + // left-hand-side, mapping a left-hand-side to a set of operations, each of + // which (together with the left-hand-side) defines an equation. + // + // All data descriptors occurring in equations are required to be present in + // the |synonymous_| equivalence relation, and to be their own representatives + // in that relation. + std::unordered_map< + const protobufs::DataDescriptor*, + std::unordered_set> + id_equations_; }; -void FactManager::DataSynonymFacts::AddFact( +void FactManager::DataSynonymAndIdEquationFacts::AddFact( const protobufs::FactDataSynonym& fact, opt::IRContext* context) { // Add the fact, including all facts relating sub-components of the data // descriptors that are involved. - AddFactRecursive(fact, context); + AddDataSynonymFactRecursive(fact.data1(), fact.data2(), context); } -void FactManager::DataSynonymFacts::AddFactRecursive( - const protobufs::FactDataSynonym& fact, opt::IRContext* context) { - assert(DataDescriptorsAreWellFormedAndComparable(context, fact.data1(), - fact.data2())); +void FactManager::DataSynonymAndIdEquationFacts::AddFact( + const protobufs::FactIdEquation& fact, opt::IRContext* context) { + protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {}); + + // Register the LHS in the equivalence relation if needed, and get a pointer + // to its representative. + if (!synonymous_.Exists(lhs_dd)) { + synonymous_.Register(lhs_dd); + } + const protobufs::DataDescriptor* lhs_dd_ptr = synonymous_.Find(&lhs_dd); + + // Get equivalence class representatives for all ids used on the RHS of the + // equation. + std::vector rhs_dd_ptrs; + for (auto rhs_id : fact.rhs_id()) { + // Register a data descriptor based on this id in the equivalence relation + // if needed, and then record the equivalence class representative. + protobufs::DataDescriptor rhs_dd = MakeDataDescriptor(rhs_id, {}); + if (!synonymous_.Exists(rhs_dd)) { + synonymous_.Register(rhs_dd); + } + rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd)); + } + // We now have the equation in a form where it refers exclusively to + // equivalence class representatives. Add it to our set of facts and work + // out any follow-on facts. + AddEquationFactRecursive(*lhs_dd_ptr, static_cast(fact.opcode()), + rhs_dd_ptrs, context); +} + +void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( + const protobufs::DataDescriptor& lhs_dd, SpvOp opcode, + const std::vector& rhs_dds, + opt::IRContext* context) { + // Precondition: all data descriptors referenced in this equation must be + // equivalence class representatives - i.e. the equation must be in canonical + // form. + assert(synonymous_.Exists(lhs_dd)); + assert(synonymous_.Find(&lhs_dd) == &lhs_dd); + for (auto rhs_dd : rhs_dds) { + (void)(rhs_dd); // Keep compilers happy in release mode. + assert(synonymous_.Exists(*rhs_dd)); + assert(synonymous_.Find(rhs_dd) == rhs_dd); + } + + if (id_equations_.count(&lhs_dd) == 0) { + // We have not seen an equation with this LHS before, so associate the LHS + // with an initially empty set. + id_equations_.insert( + {&lhs_dd, + std::unordered_set()}); + } + + { + auto existing_equations = id_equations_.find(&lhs_dd); + assert(existing_equations != id_equations_.end() && + "A set of operations should be present, even if empty."); + + Operation new_operation = {opcode, rhs_dds}; + if (existing_equations->second.count(new_operation)) { + // This equation is known, so there is nothing further to be done. + return; + } + // Add the equation to the set of known equations. + existing_equations->second.insert(new_operation); + } + + // Now try to work out corollaries implied by the new equation and existing + // facts. + switch (opcode) { + case SpvOpIAdd: { + // Equation form: "a = b + c" + { + auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]); + if (existing_first_operand_equations != id_equations_.end()) { + for (auto equation : existing_first_operand_equations->second) { + if (equation.opcode == SpvOpISub) { + // Equation form: "a = (d - e) + c" + if (equation.operands[1] == rhs_dds[1]) { + // Equation form: "a = (d - c) + c" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], + context); + } + if (equation.operands[0] == rhs_dds[1]) { + // Equation form: "a = (c - e) + c" + // We can thus infer "a = -e" + AddEquationFactRecursive(lhs_dd, SpvOpSNegate, + {equation.operands[1]}, context); + } + } + } + } + } + { + auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]); + if (existing_second_operand_equations != id_equations_.end()) { + for (auto equation : existing_second_operand_equations->second) { + if (equation.opcode == SpvOpISub) { + // Equation form: "a = b + (d - e)" + if (equation.operands[1] == rhs_dds[0]) { + // Equation form: "a = b + (d - b)" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], + context); + } + } + } + } + } + break; + } + case SpvOpISub: { + // Equation form: "a = b - c" + { + auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]); + if (existing_first_operand_equations != id_equations_.end()) { + for (auto equation : existing_first_operand_equations->second) { + if (equation.opcode == SpvOpIAdd) { + // Equation form: "a = (d + e) - c" + if (equation.operands[0] == rhs_dds[1]) { + // Equation form: "a = (c + e) - c" + // We can thus infer "a = e" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], + context); + } + if (equation.operands[1] == rhs_dds[1]) { + // Equation form: "a = (d + c) - c" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], + context); + } + } + + if (equation.opcode == SpvOpISub) { + // Equation form: "a = (d - e) - c" + if (equation.operands[0] == rhs_dds[1]) { + // Equation form: "a = (c - e) - c" + // We can thus infer "a = -e" + AddEquationFactRecursive(lhs_dd, SpvOpSNegate, + {equation.operands[1]}, context); + } + } + } + } + } + + { + auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]); + if (existing_second_operand_equations != id_equations_.end()) { + for (auto equation : existing_second_operand_equations->second) { + if (equation.opcode == SpvOpIAdd) { + // Equation form: "a = b - (d + e)" + if (equation.operands[0] == rhs_dds[0]) { + // Equation form: "a = b - (b + e)" + // We can thus infer "a = -e" + AddEquationFactRecursive(lhs_dd, SpvOpSNegate, + {equation.operands[1]}, context); + } + if (equation.operands[1] == rhs_dds[0]) { + // Equation form: "a = b - (d + b)" + // We can thus infer "a = -d" + AddEquationFactRecursive(lhs_dd, SpvOpSNegate, + {equation.operands[0]}, context); + } + } + if (equation.opcode == SpvOpISub) { + // Equation form: "a = b - (d - e)" + if (equation.operands[0] == rhs_dds[0]) { + // Equation form: "a = b - (b - e)" + // We can thus infer "a = e" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], + context); + } + } + } + } + } + break; + } + case SpvOpLogicalNot: + case SpvOpSNegate: { + // Equation form: "a = !b" or "a = -b" + auto existing_equations = id_equations_.find(rhs_dds[0]); + if (existing_equations != id_equations_.end()) { + for (auto equation : existing_equations->second) { + if (equation.opcode == opcode) { + // Equation form: "a = !!b" or "a = -(-b)" + // We can thus infer "a = b" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context); + } + } + } + break; + } + default: + break; + } +} + +void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive( + const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2, + opt::IRContext* context) { + assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2)); // Record that the data descriptors provided in the fact are equivalent. - synonymous_.MakeEquivalent(fact.data1(), fact.data2()); + synonymous_.MakeEquivalent(dd1, dd2); // As we have updated the equivalence relation, we might be able to deduce // more facts by performing a closure computation, so we record that such a // computation is required; it will be performed next time a method answering // a data synonym fact-related question is invoked. - closure_computation_required = true; + closure_computation_required_ = true; // We now check whether this is a synonym about composite objects. If it is, // we can recursively add synonym facts about their associated sub-components. @@ -428,9 +725,8 @@ void FactManager::DataSynonymFacts::AddFactRecursive( // Get the type of the object referred to by the first data descriptor in the // synonym fact. uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices( - context, - context->get_def_use_mgr()->GetDef(fact.data1().object())->type_id(), - fact.data1().index()); + context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(), + dd1.index()); auto type = context->get_type_mgr()->GetType(type_id); auto type_instruction = context->get_def_use_mgr()->GetDef(type_id); assert(type != nullptr && @@ -460,21 +756,19 @@ void FactManager::DataSynonymFacts::AddFactRecursive( // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i] for (uint32_t i = 0; i < num_composite_elements; i++) { std::vector extended_indices1 = - fuzzerutil::RepeatedFieldToVector(fact.data1().index()); + fuzzerutil::RepeatedFieldToVector(dd1.index()); extended_indices1.push_back(i); std::vector extended_indices2 = - fuzzerutil::RepeatedFieldToVector(fact.data2().index()); + fuzzerutil::RepeatedFieldToVector(dd2.index()); extended_indices2.push_back(i); - protobufs::FactDataSynonym extended_data_synonym_fact; - *extended_data_synonym_fact.mutable_data1() = - MakeDataDescriptor(fact.data1().object(), std::move(extended_indices1)); - *extended_data_synonym_fact.mutable_data2() = - MakeDataDescriptor(fact.data2().object(), std::move(extended_indices2)); - AddFactRecursive(extended_data_synonym_fact, context); + AddDataSynonymFactRecursive( + MakeDataDescriptor(dd1.object(), std::move(extended_indices1)), + MakeDataDescriptor(dd2.object(), std::move(extended_indices2)), + context); } } -void FactManager::DataSynonymFacts::ComputeClosureOfFacts( +void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts( opt::IRContext* context) const { // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct // data descriptors that describe objects of the same composite type, and that @@ -551,10 +845,10 @@ void FactManager::DataSynonymFacts::ComputeClosureOfFacts( // We keep looking for new facts until we perform a complete pass over the // equivalence relation without finding any new facts. - while (closure_computation_required) { + while (closure_computation_required_) { // We have not found any new facts yet during this pass; we set this to // 'true' if we do find a new fact. - closure_computation_required = false; + closure_computation_required_ = false; // Consider each class in the equivalence relation. for (auto representative : @@ -738,7 +1032,7 @@ void FactManager::DataSynonymFacts::ComputeClosureOfFacts( synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix); // As we have added a new synonym fact, we might benefit from doing // another pass over the equivalence relation. - closure_computation_required = true; + closure_computation_required_ = true; // Now that we know this pair of data descriptors are synonymous, // there is no point recording how close they are to being // synonymous. @@ -750,20 +1044,56 @@ void FactManager::DataSynonymFacts::ComputeClosureOfFacts( } } -bool FactManager::DataSynonymFacts::DataDescriptorsAreWellFormedAndComparable( - opt::IRContext* context, const protobufs::DataDescriptor& dd1, - const protobufs::DataDescriptor& dd2) const { - auto end_type_1 = fuzzerutil::WalkCompositeTypeIndices( +bool FactManager::DataSynonymAndIdEquationFacts:: + DataDescriptorsAreWellFormedAndComparable( + opt::IRContext* context, const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) const { + auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices( context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(), dd1.index()); - auto end_type_2 = fuzzerutil::WalkCompositeTypeIndices( + auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices( context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(), dd2.index()); - return end_type_1 && end_type_1 == end_type_2; + // The end types of the data descriptors must exist. + if (end_type_id_1 == 0 || end_type_id_2 == 0) { + return false; + } + // If the end types are the same, the data descriptors are comparable. + if (end_type_id_1 == end_type_id_2) { + return true; + } + // Otherwise they are only comparable if they are integer scalars or integer + // vectors that differ only in signedness. + + // Get both types. + const opt::analysis::Type* type_1 = + context->get_type_mgr()->GetType(end_type_id_1); + const opt::analysis::Type* type_2 = + context->get_type_mgr()->GetType(end_type_id_2); + + // If the first type is a vector, check that the second type is a vector of + // the same width, and drill down to the vector element types. + if (type_1->AsVector()) { + if (!type_2->AsVector()) { + return false; + } + if (type_1->AsVector()->element_count() != + type_2->AsVector()->element_count()) { + return false; + } + type_1 = type_1->AsVector()->element_type(); + type_2 = type_2->AsVector()->element_type(); + } + // Check that type_1 and type_2 are both integer types of the same bit-width + // (but with potentially different signedness). + auto integer_type_1 = type_1->AsInteger(); + auto integer_type_2 = type_2->AsInteger(); + return integer_type_1 && integer_type_2 && + integer_type_1->width() == integer_type_2->width(); } std::vector -FactManager::DataSynonymFacts::GetSynonymsForDataDescriptor( +FactManager::DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor( const protobufs::DataDescriptor& data_descriptor, opt::IRContext* context) const { ComputeClosureOfFacts(context); @@ -774,7 +1104,7 @@ FactManager::DataSynonymFacts::GetSynonymsForDataDescriptor( } std::vector -FactManager::DataSynonymFacts ::GetIdsForWhichSynonymsAreKnown( +FactManager::DataSynonymAndIdEquationFacts ::GetIdsForWhichSynonymsAreKnown( opt::IRContext* context) const { ComputeClosureOfFacts(context); std::vector result; @@ -786,12 +1116,12 @@ FactManager::DataSynonymFacts ::GetIdsForWhichSynonymsAreKnown( return result; } -bool FactManager::DataSynonymFacts::IsSynonymous( +bool FactManager::DataSynonymAndIdEquationFacts::IsSynonymous( const protobufs::DataDescriptor& data_descriptor1, const protobufs::DataDescriptor& data_descriptor2, opt::IRContext* context) const { - const_cast(this)->ComputeClosureOfFacts( - context); + const_cast(this) + ->ComputeClosureOfFacts(context); return synonymous_.Exists(data_descriptor1) && synonymous_.Exists(data_descriptor2) && synonymous_.IsEquivalent(data_descriptor1, data_descriptor2); @@ -891,7 +1221,8 @@ bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant( FactManager::FactManager() : uniform_constant_facts_(MakeUnique()), - data_synonym_facts_(MakeUnique()), + data_synonym_and_id_equation_facts_( + MakeUnique()), dead_block_facts_(MakeUnique()), livesafe_function_facts_(MakeUnique()), irrelevant_pointee_value_facts_( @@ -918,7 +1249,8 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact, return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(), context); case protobufs::Fact::kDataSynonymFact: - data_synonym_facts_->AddFact(fact.data_synonym_fact(), context); + data_synonym_and_id_equation_facts_->AddFact(fact.data_synonym_fact(), + context); return true; case protobufs::Fact::kBlockIsDeadFact: dead_block_facts_->AddFact(fact.block_is_dead_fact()); @@ -938,7 +1270,7 @@ void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1, protobufs::FactDataSynonym fact; *fact.mutable_data1() = data1; *fact.mutable_data2() = data2; - data_synonym_facts_->AddFact(fact, context); + data_synonym_and_id_equation_facts_->AddFact(fact, context); } std::vector FactManager::GetConstantsAvailableFromUniformsForType( @@ -973,15 +1305,16 @@ FactManager::GetConstantUniformFactsAndTypes() const { std::vector FactManager::GetIdsForWhichSynonymsAreKnown( opt::IRContext* context) const { - return data_synonym_facts_->GetIdsForWhichSynonymsAreKnown(context); + return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown( + context); } std::vector FactManager::GetSynonymsForDataDescriptor( const protobufs::DataDescriptor& data_descriptor, opt::IRContext* context) const { - return data_synonym_facts_->GetSynonymsForDataDescriptor(data_descriptor, - context); + return data_synonym_and_id_equation_facts_->GetSynonymsForDataDescriptor( + data_descriptor, context); } std::vector FactManager::GetSynonymsForId( @@ -993,8 +1326,8 @@ bool FactManager::IsSynonymous( const protobufs::DataDescriptor& data_descriptor1, const protobufs::DataDescriptor& data_descriptor2, opt::IRContext* context) const { - return data_synonym_facts_->IsSynonymous(data_descriptor1, data_descriptor2, - context); + return data_synonym_and_id_equation_facts_->IsSynonymous( + data_descriptor1, data_descriptor2, context); } bool FactManager::BlockIsDead(uint32_t block_id) const { @@ -1027,5 +1360,17 @@ void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) { irrelevant_pointee_value_facts_->AddFact(fact); } +void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode, + const std::vector& rhs_id, + opt::IRContext* context) { + protobufs::FactIdEquation fact; + fact.set_lhs_id(lhs_id); + fact.set_opcode(opcode); + for (auto an_rhs_id : rhs_id) { + fact.add_rhs_id(an_rhs_id); + } + data_synonym_and_id_equation_facts_->AddFact(fact, context); +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h index 55cbfa04ef..f80d6773a7 100644 --- a/source/fuzz/fact_manager.h +++ b/source/fuzz/fact_manager.h @@ -68,6 +68,14 @@ class FactManager { // is irrelevant: it does not affect the observable behaviour of the module. void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id); + // Records the fact that |lhs_id| is defined by the equation: + // + // |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]| + // + void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode, + const std::vector& rhs_id, + opt::IRContext* context); + // The fact manager is responsible for managing a few distinct categories of // facts. In principle there could be different fact managers for each kind // of fact, but in practice providing one 'go to' place for facts is @@ -179,9 +187,10 @@ class FactManager { std::unique_ptr uniform_constant_facts_; // Unique pointer to internal data. - class DataSynonymFacts; // Opaque class for management of data synonym facts. - std::unique_ptr - data_synonym_facts_; // Unique pointer to internal data. + class DataSynonymAndIdEquationFacts; // Opaque class for management of data + // synonym and id equation facts. + std::unique_ptr + data_synonym_and_id_equation_facts_; // Unique pointer to internal data. class DeadBlockFacts; // Opaque class for management of dead block facts. std::unique_ptr diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 6c2821c6e2..b702025ab7 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -26,6 +26,7 @@ #include "source/fuzz/fuzzer_pass_add_dead_blocks.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_equation_instructions.h" #include "source/fuzz/fuzzer_pass_add_function_calls.h" #include "source/fuzz/fuzzer_pass_add_global_variables.h" #include "source/fuzz/fuzzer_pass_add_loads.h" @@ -200,6 +201,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 12657720c3..a374f299e7 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -30,6 +30,8 @@ const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; const std::pair kChanceOfAddingDeadBlock = {20, 90}; const std::pair kChanceOfAddingDeadBreak = {5, 80}; const std::pair kChanceOfAddingDeadContinue = {5, 80}; +const std::pair kChanceOfAddingEquationInstruction = {5, + 90}; const std::pair kChanceOfAddingGlobalVariable = {20, 90}; const std::pair kChanceOfAddingLoad = {5, 50}; const std::pair kChanceOfAddingLocalVariable = {20, 90}; @@ -102,6 +104,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak); chance_of_adding_dead_continue_ = ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue); + chance_of_adding_equation_instruction_ = + ChooseBetweenMinAndMax(kChanceOfAddingEquationInstruction); chance_of_adding_global_variable_ = ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable); chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad); diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 23127ff97b..1d8b7bf2e0 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -82,6 +82,9 @@ class FuzzerContext { uint32_t GetChanceOfAddingDeadContinue() { return chance_of_adding_dead_continue_; } + uint32_t GetChanceOfAddingEquationInstruction() { + return chance_of_adding_equation_instruction_; + } uint32_t GetChanceOfAddingGlobalVariable() { return chance_of_adding_global_variable_; } @@ -177,6 +180,7 @@ class FuzzerContext { uint32_t chance_of_adding_dead_block_; uint32_t chance_of_adding_dead_break_; uint32_t chance_of_adding_dead_continue_; + uint32_t chance_of_adding_equation_instruction_; uint32_t chance_of_adding_global_variable_; uint32_t chance_of_adding_load_; uint32_t chance_of_adding_local_variable_; diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp new file mode 100644 index 0000000000..5ffc31c784 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -0,0 +1,235 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_equation_instructions.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_equation_instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() = + default; + +void FuzzerPassAddEquationInstructions::Apply() { + MaybeAddTransformationBeforeEachInstruction( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingEquationInstruction())) { + return; + } + + // Check that it is OK to add an equation instruction before the given + // instruction in principle - e.g. check that this does not lead to + // inserting before an OpVariable or OpPhi instruction. We use OpIAdd + // as an example opcode for this check, to be representative of *some* + // opcode that defines an equation, even though we may choose a + // different opcode below. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) { + return; + } + + // Get all available instructions with result ids and types that are not + // OpUndef. + std::vector available_instructions = + FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext*, opt::Instruction* instruction) -> bool { + return instruction->result_id() && instruction->type_id() && + instruction->opcode() != SpvOpUndef; + }); + + // Try the opcodes for which we know how to make ids at random until + // something works. + std::vector candidate_opcodes = {SpvOpIAdd, SpvOpISub, + SpvOpLogicalNot, SpvOpSNegate}; + do { + auto opcode = + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes); + switch (opcode) { + case SpvOpIAdd: + case SpvOpISub: { + // Instructions of integer (scalar or vector) result type are + // suitable for these opcodes. + auto integer_instructions = + GetIntegerInstructions(available_instructions); + if (!integer_instructions.empty()) { + // There is at least one such instruction, so pick one at random + // for the LHS of an equation. + auto lhs = integer_instructions.at( + GetFuzzerContext()->RandomIndex(integer_instructions)); + + // For the RHS, we can use any instruction with an integer + // scalar/vector result type of the same number of components + // and the same bit-width for the underlying integer type. + + // Work out the element count and bit-width. + auto lhs_type = + GetIRContext()->get_type_mgr()->GetType(lhs->type_id()); + uint32_t lhs_element_count; + uint32_t lhs_bit_width; + if (lhs_type->AsVector()) { + lhs_element_count = lhs_type->AsVector()->element_count(); + lhs_bit_width = lhs_type->AsVector() + ->element_type() + ->AsInteger() + ->width(); + } else { + lhs_element_count = 1; + lhs_bit_width = lhs_type->AsInteger()->width(); + } + + // Get all the instructions that match on element count and + // bit-width. + auto candidate_rhs_instructions = RestrictToElementBitWidth( + RestrictToVectorWidth(integer_instructions, + lhs_element_count), + lhs_bit_width); + + // Choose a RHS instruction at random; there is guaranteed to + // be at least one choice as the LHS will be available. + auto rhs = candidate_rhs_instructions.at( + GetFuzzerContext()->RandomIndex( + candidate_rhs_instructions)); + + // Add the equation instruction. + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {lhs->result_id(), rhs->result_id()}, + instruction_descriptor)); + return; + } + break; + } + case SpvOpLogicalNot: { + // Choose any available instruction of boolean scalar/vector + // result type and equate its negation with a fresh id. + auto boolean_instructions = + GetBooleanInstructions(available_instructions); + if (!boolean_instructions.empty()) { + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {boolean_instructions + .at(GetFuzzerContext()->RandomIndex( + boolean_instructions)) + ->result_id()}, + instruction_descriptor)); + return; + } + break; + } + case SpvOpSNegate: { + // Similar to OpLogicalNot, but for signed integer negation. + auto integer_instructions = + GetIntegerInstructions(available_instructions); + if (!integer_instructions.empty()) { + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {integer_instructions + .at(GetFuzzerContext()->RandomIndex( + integer_instructions)) + ->result_id()}, + instruction_descriptor)); + return; + } + break; + } + default: + assert(false && "Unexpected opcode."); + break; + } + } while (!candidate_opcodes.empty()); + // Reaching here means that we did not manage to apply any + // transformation at this point of the module. + }); +} + +std::vector +FuzzerPassAddEquationInstructions::GetIntegerInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsInteger() || + (type->AsVector() && type->AsVector()->element_type()->AsInteger())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::GetBooleanInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsBool() || + (type->AsVector() && type->AsVector()->element_type()->AsBool())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::RestrictToVectorWidth( + const std::vector& instructions, + uint32_t vector_width) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if ((vector_width == 1 && !type->AsVector()) || + (vector_width > 1 && + type->AsVector()->element_count() == vector_width)) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::RestrictToElementBitWidth( + const std::vector& instructions, + uint32_t bit_width) const { + std::vector result; + for (auto& inst : instructions) { + const opt::analysis::Type* type = + GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsVector()) { + type = type->AsVector()->element_type(); + } + assert(type->AsInteger() && + "Precondition: all input instructions must " + "have integer scalar or vector type."); + if (type->AsInteger()->width() == bit_width) { + result.push_back(inst); + } + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h new file mode 100644 index 0000000000..84229c05c3 --- /dev/null +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -0,0 +1,67 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that sprinkles instructions through the module that define +// equations using various arithmetic and logical operators. +class FuzzerPassAddEquationInstructions : public FuzzerPass { + public: + FuzzerPassAddEquationInstructions( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddEquationInstructions(); + + void Apply() override; + + private: + // Yields those instructions in |instructions| that have integer scalar or + // vector result type. + std::vector GetIntegerInstructions( + const std::vector& instructions) const; + + // Yields those instructions in |instructions| that have boolean scalar or + // vector result type. + std::vector GetBooleanInstructions( + const std::vector& instructions) const; + + // Requires that |instructions| are scalars or vectors of some type. Returns + // only those instructions whose width is |width|. If |width| is 1 this means + // the scalars. + std::vector RestrictToVectorWidth( + const std::vector& instructions, + uint32_t vector_width) const; + + // Requires that |instructions| are integer scalars or vectors. Returns only + // those instructions for which the bit-width of the underlying integer type + // is |bit_width|. + std::vector RestrictToElementBitWidth( + const std::vector& instructions, + uint32_t bit_width) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 1575fa99e3..e7e9976700 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -85,15 +85,18 @@ void FuzzerPassApplyIdSynonyms::Apply() { auto synonym_to_try = synonyms_to_try[synonym_index]; synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index); + // If the synonym's |index_size| is zero, the synonym represents an id. + // Otherwise it represents some element of a composite structure, in + // which case we need to be able to add an extract instruction to get + // that element out. if (synonym_to_try->index_size() > 0 && - use_inst->opcode() == SpvOpPhi) { - // We are trying to replace an operand to an OpPhi. This means - // we cannot use a composite synonym, because that requires - // extracting a component from a composite and we cannot insert - // an extract instruction before an OpPhi. + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + use_inst)) { + // We cannot insert an extract before this instruction, so this + // synonym is no good. // - // TODO(afd): We could consider inserting the extract instruction - // into the relevant parent block of the OpPhi. + // TODO(afd): In the case of an OpPhi, we could consider inserting the + // extract instruction into the relevant parent block of the OpPhi. continue; } @@ -103,30 +106,25 @@ void FuzzerPassApplyIdSynonyms::Apply() { continue; } - // We either replace the use with an id known to be synonymous, or - // an id that will hold the result of extracting a synonym from a - // composite. + // We either replace the use with an id known to be synonymous (when + // the synonym's |index_size| is 0), or an id that will hold the result + // of extracting a synonym from a composite (when the synonym's + // |index_size| is > 0). uint32_t id_with_which_to_replace_use; if (synonym_to_try->index_size() == 0) { id_with_which_to_replace_use = synonym_to_try->object(); } else { id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId(); - protobufs::InstructionDescriptor instruction_to_insert_before = - MakeInstructionDescriptor(GetIRContext(), use_inst); - TransformationCompositeExtract composite_extract_transformation( - instruction_to_insert_before, id_with_which_to_replace_use, - synonym_to_try->object(), - fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())); - ApplyTransformation(composite_extract_transformation); + ApplyTransformation(TransformationCompositeExtract( + MakeInstructionDescriptor(GetIRContext(), use_inst), + id_with_which_to_replace_use, synonym_to_try->object(), + fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()))); } - TransformationReplaceIdWithSynonym replace_id_transformation( + ApplyTransformation(TransformationReplaceIdWithSynonym( MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, use_in_operand_index), - id_with_which_to_replace_use); - - // The transformation should be applicable by construction. - ApplyTransformation(replace_id_transformation); + id_with_which_to_replace_use)); break; } } diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 9773b60674..61c66c2b03 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -168,12 +168,22 @@ message Fact { FactDataSynonym data_synonym_fact = 2; FactBlockIsDead block_is_dead_fact = 3; FactFunctionIsLivesafe function_is_livesafe_fact = 4; - FactPointeeValueIsIrrelevant pointee_value_is_irrelevant = 5; + FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5; + FactIdEquation id_equation_fact = 6; } } // Keep fact message types in alphabetical order: +message FactBlockIsDead { + + // Records the fact that a block is guaranteed to be dynamically unreachable. + // This is useful because it informs the fuzzer that rather arbitrary changes + // can be made to this block. + + uint32 block_id = 1; +} + message FactConstantUniform { // Records the fact that a uniform buffer element is guaranteed to be equal @@ -203,15 +213,6 @@ message FactDataSynonym { } -message FactBlockIsDead { - - // Records the fact that a block is guaranteed to be dynamically unreachable. - // This is useful because it informs the fuzzer that rather arbitrary changes - // can be made to this block. - - uint32 block_id = 1; -} - message FactFunctionIsLivesafe { // Records the fact that a function is guaranteed to be "livesafe", meaning @@ -223,6 +224,31 @@ message FactFunctionIsLivesafe { uint32 function_id = 1; } +message FactIdEquation { + + // Records the fact that the equation: + // + // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1] + // + // holds; e.g. that the equation: + // + // %12 = OpIAdd %13 %14 + // + // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is + // OpIAdd. + + // The left-hand-side of the equation. + uint32 lhs_id = 1; + + // A SPIR-V opcode, from a restricted set of instructions for which equation + // facts make sense. + uint32 opcode = 2; + + // The operands to the right-hand-side of the equation. + repeated uint32 rhs_id = 3; + +} + message FactPointeeValueIsIrrelevant { // Records the fact that value of the data pointed to by a pointer id does @@ -342,6 +368,7 @@ message Transformation { TransformationStore store = 37; TransformationFunctionCall function_call = 38; TransformationAccessChain access_chain = 39; + TransformationEquationInstruction equation_instruction = 40; // Add additional option using the next available number. } } @@ -752,6 +779,28 @@ message TransformationCopyObject { } +message TransformationEquationInstruction { + + // A transformation that adds an instruction to the module that defines an + // equation between its result id and input operand ids, such that the + // equation is guaranteed to hold at any program point where all ids involved + // are available (i.e. at any program point dominated by the instruction). + + // The result id of the new instruction + uint32 fresh_id = 1; + + // The instruction's opcode + uint32 opcode = 2; + + // The input operands to the instruction + repeated uint32 in_operand_id = 3; + + // A descriptor for an instruction in a block before which the new + // instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + +} + message TransformationFunctionCall { // A transformation that introduces an OpFunctionCall instruction. The call diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index 52fcfd73da..a69da75649 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -41,6 +41,7 @@ #include "source/fuzz/transformation_composite_construct.h" #include "source/fuzz/transformation_composite_extract.h" #include "source/fuzz/transformation_copy_object.h" +#include "source/fuzz/transformation_equation_instruction.h" #include "source/fuzz/transformation_function_call.h" #include "source/fuzz/transformation_load.h" #include "source/fuzz/transformation_merge_blocks.h" @@ -128,6 +129,9 @@ std::unique_ptr Transformation::FromMessage( message.composite_extract()); case protobufs::Transformation::TransformationCase::kCopyObject: return MakeUnique(message.copy_object()); + case protobufs::Transformation::TransformationCase::kEquationInstruction: + return MakeUnique( + message.equation_instruction()); case protobufs::Transformation::TransformationCase::kFunctionCall: return MakeUnique(message.function_call()); case protobufs::Transformation::TransformationCase::kLoad: diff --git a/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp new file mode 100644 index 0000000000..21b67f66e0 --- /dev/null +++ b/source/fuzz/transformation_equation_instruction.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_equation_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationEquationInstruction::TransformationEquationInstruction( + const spvtools::fuzz::protobufs::TransformationEquationInstruction& message) + : message_(message) {} + +TransformationEquationInstruction::TransformationEquationInstruction( + uint32_t fresh_id, SpvOp opcode, const std::vector& in_operand_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_opcode(opcode); + for (auto id : in_operand_id) { + message_.add_in_operand_id(id); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationEquationInstruction::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + // The instruction to insert before must exist. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), context); + if (!insert_before) { + return false; + } + // The input ids must all exist, not be OpUndef, and be available before this + // instruction. + for (auto id : message_.in_operand_id()) { + auto inst = context->get_def_use_mgr()->GetDef(id); + if (!inst) { + return false; + } + if (inst->opcode() == SpvOpUndef) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + id)) { + return false; + } + } + + return MaybeGetResultType(context) != 0; +} + +void TransformationEquationInstruction::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + + opt::Instruction::OperandList in_operands; + std::vector rhs_id; + for (auto id : message_.in_operand_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + rhs_id.push_back(id); + } + + FindInstruction(message_.instruction_to_insert_before(), context) + ->InsertBefore(MakeUnique( + context, static_cast(message_.opcode()), + MaybeGetResultType(context), message_.fresh_id(), in_operands)); + + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + fact_manager->AddFactIdEquation(message_.fresh_id(), + static_cast(message_.opcode()), rhs_id, + context); +} + +protobufs::Transformation TransformationEquationInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_equation_instruction() = message_; + return result; +} + +uint32_t TransformationEquationInstruction::MaybeGetResultType( + opt::IRContext* context) const { + switch (static_cast(message_.opcode())) { + case SpvOpIAdd: + case SpvOpISub: { + if (message_.in_operand_id().size() != 2) { + return 0; + } + uint32_t first_operand_width = 0; + uint32_t first_operand_type_id = 0; + for (uint32_t index = 0; index < 2; index++) { + auto operand_inst = + context->get_def_use_mgr()->GetDef(message_.in_operand_id(index)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsInteger() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsInteger()))) { + return 0; + } + uint32_t operand_width = + operand_type->AsInteger() + ? 1 + : operand_type->AsVector()->element_count(); + if (index == 0) { + first_operand_width = operand_width; + first_operand_type_id = operand_inst->type_id(); + } else { + assert(first_operand_width != 0 && + "The first operand should have been processed."); + if (operand_width != first_operand_width) { + return 0; + } + } + } + assert(first_operand_type_id != 0 && + "A type must have been found for the first operand."); + return first_operand_type_id; + } + case SpvOpLogicalNot: { + if (message_.in_operand_id().size() != 1) { + return 0; + } + auto operand_inst = + context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsBool() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsBool()))) { + return 0; + } + return operand_inst->type_id(); + } + case SpvOpSNegate: { + if (message_.in_operand_id().size() != 1) { + return 0; + } + auto operand_inst = + context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsInteger() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsInteger()))) { + return 0; + } + return operand_inst->type_id(); + } + + default: + assert(false && "Inappropriate opcode for equation instruction."); + return 0; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_equation_instruction.h b/source/fuzz/transformation_equation_instruction.h new file mode 100644 index 0000000000..2456ba5092 --- /dev/null +++ b/source/fuzz/transformation_equation_instruction.h @@ -0,0 +1,76 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ + +#include + +#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 TransformationEquationInstruction : public Transformation { + public: + explicit TransformationEquationInstruction( + const protobufs::TransformationEquationInstruction& message); + + TransformationEquationInstruction( + uint32_t fresh_id, SpvOp opcode, + const std::vector& in_operand_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh. + // - |message_.instruction_to_insert_before| must identify an instruction + // before which an equation instruction can legitimately be inserted. + // - Each id in |message_.in_operand_id| must exist, not be an OpUndef, and + // be available before |message_.instruction_to_insert_before|. + // - |message_.opcode| must be an opcode for which we know how to handle + // equations, the types of the ids in |message_.in_operand_id| must be + // suitable for use with this opcode, and the module must contain an + // appropriate result type id. + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an instruction to the module, right before + // |message_.instruction_to_insert_before|, of the form: + // + // |message_.fresh_id| = |message_.opcode| %type |message_.in_operand_ids| + // + // where %type is a type id that already exists in the module and that is + // compatible with the opcode and input operands. + // + // The fact manager is also updated to inform it of this equation fact. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + // A helper that, in one fell swoop, checks that |message_.opcode| and the ids + // in |message_.in_operand_id| are compatible, and that the module contains + // an appropriate result type id. If all is well, the result type id is + // returned. Otherwise, 0 is returned. + uint32_t MaybeGetResultType(opt::IRContext* context) const; + + protobufs::TransformationEquationInstruction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 4211ff2bcb..6ec4c2ad4d 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -48,6 +48,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_composite_construct_test.cpp transformation_composite_extract_test.cpp transformation_copy_object_test.cpp + transformation_equation_instruction_test.cpp transformation_function_call_test.cpp transformation_load_test.cpp transformation_merge_blocks_test.cpp diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp index b3f32cd513..2c79f128b5 100644 --- a/test/fuzz/fact_manager_test.cpp +++ b/test/fuzz/fact_manager_test.cpp @@ -1173,6 +1173,206 @@ TEST(FactManagerTest, RecursiveAdditionOfFacts) { context.get())); } +TEST(FactManagerTest, LogicalNotEquationFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpLogicalNot %6 %7 + %15 = OpCopyObject %6 %7 + %16 = OpCopyObject %6 %14 + %17 = OpLogicalNot %6 %16 + 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; + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}), + MakeDataDescriptor(7, {}), context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}), + MakeDataDescriptor(14, {}), context.get()); + fact_manager.AddFactIdEquation(14, SpvOpLogicalNot, {7}, context.get()); + fact_manager.AddFactIdEquation(17, SpvOpLogicalNot, {16}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(7, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(17, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(16, {}), MakeDataDescriptor(14, {}), context.get())); +} + +TEST(FactManagerTest, SignedNegateEquationFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpSNegate %6 %7 + %15 = OpSNegate %6 %14 + 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; + + fact_manager.AddFactIdEquation(14, SpvOpSNegate, {7}, context.get()); + fact_manager.AddFactIdEquation(15, SpvOpSNegate, {14}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(7, {}), MakeDataDescriptor(15, {}), context.get())); +} + +TEST(FactManagerTest, AddSubNegateFacts1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpIAdd %6 %15 %16 + %17 = OpCopyObject %6 %15 + %18 = OpCopyObject %6 %16 + %19 = OpISub %6 %14 %18 ; ==> synonymous(%19, %15) + %20 = OpISub %6 %14 %17 ; ==> synonymous(%20, %16) + %21 = OpCopyObject %6 %14 + %22 = OpISub %6 %16 %21 + %23 = OpCopyObject %6 %22 + %24 = OpSNegate %6 %23 ; ==> synonymous(%24, %15) + 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; + + fact_manager.AddFactIdEquation(14, SpvOpIAdd, {15, 16}, context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}), + MakeDataDescriptor(15, {}), context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}), + MakeDataDescriptor(16, {}), context.get()); + fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 18}, context.get()); + fact_manager.AddFactIdEquation(20, SpvOpISub, {14, 17}, context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}), + MakeDataDescriptor(14, {}), context.get()); + fact_manager.AddFactIdEquation(22, SpvOpISub, {16, 21}, context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}), + MakeDataDescriptor(22, {}), context.get()); + fact_manager.AddFactIdEquation(24, SpvOpSNegate, {23}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(19, {}), MakeDataDescriptor(15, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get())); +} + +TEST(FactManagerTest, AddSubNegateFacts2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpISub %6 %15 %16 + %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15) + %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15) + %19 = OpISub %6 %14 %15 + %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16) + %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15) + %22 = OpISub %6 %14 %18 + %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16) + 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; + + fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get()); + fact_manager.AddFactIdEquation(17, SpvOpIAdd, {14, 16}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get())); + + fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 14}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get())); + + fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get()); + fact_manager.AddFactIdEquation(20, SpvOpSNegate, {19}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get())); + + fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get()); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get())); + + fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get()); + fact_manager.AddFactIdEquation(23, SpvOpSNegate, {22}, context.get()); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp new file mode 100644 index 0000000000..81d849b465 --- /dev/null +++ b/test/fuzz/transformation_equation_instruction_test.cpp @@ -0,0 +1,462 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_equation_instruction.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationEquationInstructionTest, SignedNegate) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 24 + %40 = OpTypeBool + %41 = OpConstantTrue %40 + %20 = OpUndef %6 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %30 = OpCopyObject %6 %7 + 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; + + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Bad: id already in use. + ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + // Bad: identified instruction does not exist. + ASSERT_FALSE( + TransformationEquationInstruction( + 14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: id 100 does not exist + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + // Bad: id 20 is an OpUndef + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + // Bad: id 30 is not available right before its definition + ASSERT_FALSE(TransformationEquationInstruction( + 14, SpvOpSNegate, {30}, + MakeInstructionDescriptor(30, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), fact_manager)); + + // Bad: too many arguments to OpSNegate. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + // Bad: 40 is a type id. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + // Bad: wrong type of argument to OpSNegate. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpSNegate, {7}, return_instruction); + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation2 = TransformationEquationInstruction( + 15, SpvOpSNegate, {14}, return_instruction); + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 24 + %40 = OpTypeBool + %41 = OpConstantTrue %40 + %20 = OpUndef %6 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %30 = OpCopyObject %6 %7 + %14 = OpSNegate %6 %7 + %15 = OpSNegate %6 %14 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, LogicalNot) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 5 + %12 = OpFunction %2 None %3 + %13 = OpLabel + 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; + + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Bad: too few arguments to OpLogicalNot. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + // Bad: 6 is a type id. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + // Bad: wrong type of argument to OpLogicalNot. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpLogicalNot, {7}, return_instruction); + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation2 = TransformationEquationInstruction( + 15, SpvOpLogicalNot, {14}, return_instruction); + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 5 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpLogicalNot %6 %7 + %15 = OpLogicalNot %6 %14 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, AddSubNegate1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %30 = OpTypeVector %6 3 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %31 = OpConstantComposite %30 %15 %16 %15 + %33 = OpTypeBool + %32 = OpConstantTrue %33 + %12 = OpFunction %2 None %3 + %13 = OpLabel + 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; + + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Bad: too many arguments to OpIAdd. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + // Bad: boolean argument to OpIAdd. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + // Bad: type as argument to OpIAdd. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + // Bad: arguments of mismatched widths + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + // Bad: arguments of mismatched widths + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15}, + return_instruction) + .IsApplicable(context.get(), fact_manager)); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpIAdd, {15, 16}, return_instruction); + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation2 = TransformationEquationInstruction( + 19, SpvOpISub, {14, 16}, return_instruction); + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}), context.get())); + + auto transformation3 = TransformationEquationInstruction( + 20, SpvOpISub, {14, 15}, return_instruction); + ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); + transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get())); + + auto transformation4 = TransformationEquationInstruction( + 22, SpvOpISub, {16, 14}, return_instruction); + ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); + transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation5 = TransformationEquationInstruction( + 24, SpvOpSNegate, {22}, return_instruction); + ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); + transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %30 = OpTypeVector %6 3 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %31 = OpConstantComposite %30 %15 %16 %15 + %33 = OpTypeBool + %32 = OpConstantTrue %33 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpIAdd %6 %15 %16 + %19 = OpISub %6 %14 %16 ; ==> synonymous(%19, %15) + %20 = OpISub %6 %14 %15 ; ==> synonymous(%20, %16) + %22 = OpISub %6 %16 %14 + %24 = OpSNegate %6 %22 ; ==> synonymous(%24, %15) + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, AddSubNegate2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + 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; + + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpISub, {15, 16}, return_instruction); + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation2 = TransformationEquationInstruction( + 17, SpvOpIAdd, {14, 16}, return_instruction); + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get())); + + auto transformation3 = TransformationEquationInstruction( + 18, SpvOpIAdd, {16, 14}, return_instruction); + ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); + transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get())); + + auto transformation4 = TransformationEquationInstruction( + 19, SpvOpISub, {14, 15}, return_instruction); + ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); + transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation5 = TransformationEquationInstruction( + 20, SpvOpSNegate, {19}, return_instruction); + ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); + transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get())); + + auto transformation6 = TransformationEquationInstruction( + 21, SpvOpISub, {14, 19}, return_instruction); + ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); + transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get())); + + auto transformation7 = TransformationEquationInstruction( + 22, SpvOpISub, {14, 18}, return_instruction); + ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager)); + transformation7.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation8 = TransformationEquationInstruction( + 23, SpvOpSNegate, {22}, return_instruction); + ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager)); + transformation8.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpISub %6 %15 %16 + %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15) + %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15) + %19 = OpISub %6 %14 %15 + %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16) + %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15) + %22 = OpISub %6 %14 %18 + %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16) + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools From 66a682b6a8f973ba79b0e250221d53a4fbd11f4c Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 5 Mar 2020 08:18:39 +0000 Subject: [PATCH 36/88] spirv-fuzz: Add swap commutable operands transformation (#3205) In this PR, the classes that represent the swap commutable operands transformation and the fuzzer pass were implemented. Fixes #3205. --- source/fuzz/CMakeLists.txt | 4 + source/fuzz/fuzzer.cpp | 4 + .../fuzzer_pass_swap_commutable_operands.cpp | 50 ++ .../fuzzer_pass_swap_commutable_operands.h | 41 ++ source/fuzz/protobufs/spvtoolsfuzz.proto | 10 + source/fuzz/transformation.cpp | 4 + ...ransformation_swap_commutable_operands.cpp | 66 +++ .../transformation_swap_commutable_operands.h | 51 +++ source/opcode.cpp | 33 ++ source/opcode.h | 4 + test/fuzz/CMakeLists.txt | 1 + ...ormation_swap_commutable_operands_test.cpp | 427 ++++++++++++++++++ utils/check_copyright.py | 6 +- 13 files changed, 699 insertions(+), 2 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_swap_commutable_operands.cpp create mode 100644 source/fuzz/fuzzer_pass_swap_commutable_operands.h create mode 100644 source/fuzz/transformation_swap_commutable_operands.cpp create mode 100644 source/fuzz/transformation_swap_commutable_operands.h create mode 100644 test/fuzz/transformation_swap_commutable_operands_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 5b2afe4f17..231ea75750 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -63,6 +63,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_outline_functions.h fuzzer_pass_permute_blocks.h fuzzer_pass_split_blocks.h + fuzzer_pass_swap_commutable_operands.h fuzzer_util.h id_use_descriptor.h instruction_descriptor.h @@ -112,6 +113,7 @@ if(SPIRV_BUILD_FUZZER) transformation_set_selection_control.h transformation_split_block.h transformation_store.h + transformation_swap_commutable_operands.h transformation_vector_shuffle.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -149,6 +151,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_outline_functions.cpp fuzzer_pass_permute_blocks.cpp fuzzer_pass_split_blocks.cpp + fuzzer_pass_swap_commutable_operands.cpp fuzzer_util.cpp id_use_descriptor.cpp instruction_descriptor.cpp @@ -197,6 +200,7 @@ if(SPIRV_BUILD_FUZZER) transformation_set_selection_control.cpp transformation_split_block.cpp transformation_store.cpp + transformation_swap_commutable_operands.cpp transformation_vector_shuffle.cpp uniform_buffer_element_descriptor.cpp ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index b702025ab7..945bcc59c8 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -46,6 +46,7 @@ #include "source/fuzz/fuzzer_pass_outline_functions.h" #include "source/fuzz/fuzzer_pass_permute_blocks.h" #include "source/fuzz/fuzzer_pass_split_blocks.h" +#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/pseudo_random_generator.h" #include "source/opt/build_module.h" @@ -280,6 +281,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); for (auto& pass : final_passes) { if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) { return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp new file mode 100644 index 0000000000..4df97c94e2 --- /dev/null +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_swap_commutable_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_commutable_operands.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default; + +void FuzzerPassSwapCommutableOperands::Apply() { + auto context = GetIRContext(); + // Iterates over the module's instructions and checks whether it is + // commutative. In this case, the transformation is probabilistically applied. + context->module()->ForEachInst( + [this, context](opt::Instruction* instruction) { + if (spvOpcodeIsCommutativeBinaryOperator(instruction->opcode()) && + GetFuzzerContext()->ChooseEven()) { + auto instructionDescriptor = + MakeInstructionDescriptor(context, instruction); + auto transformation = + TransformationSwapCommutableOperands(instructionDescriptor); + ApplyTransformation(transformation); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h new file mode 100644 index 0000000000..b0206de760 --- /dev/null +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_SWAP_COMMUTABLE_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for all commutative instructions in the module, +// probabilistically choosing which of these instructions will have its input +// operands swapped. +class FuzzerPassSwapCommutableOperands : public FuzzerPass { + public: + FuzzerPassSwapCommutableOperands( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassSwapCommutableOperands(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 61c66c2b03..25f0142039 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -369,6 +369,7 @@ message Transformation { TransformationFunctionCall function_call = 38; TransformationAccessChain access_chain = 39; TransformationEquationInstruction equation_instruction = 40; + TransformationSwapCommutableOperands swap_commutable_operands = 41; // Add additional option using the next available number. } } @@ -1068,6 +1069,15 @@ message TransformationStore { } +message TransformationSwapCommutableOperands { + + // A transformation that swaps the operands of a commutative instruction. + + // A descriptor for a commutative instruction + InstructionDescriptor instruction_descriptor = 1; + +} + message TransformationVectorShuffle { // A transformation that adds a vector shuffle instruction. diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index a69da75649..fe0d41c806 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -56,6 +56,7 @@ #include "source/fuzz/transformation_set_selection_control.h" #include "source/fuzz/transformation_split_block.h" #include "source/fuzz/transformation_store.h" +#include "source/fuzz/transformation_swap_commutable_operands.h" #include "source/fuzz/transformation_vector_shuffle.h" #include "source/util/make_unique.h" @@ -170,6 +171,9 @@ std::unique_ptr Transformation::FromMessage( return MakeUnique(message.split_block()); case protobufs::Transformation::TransformationCase::kStore: return MakeUnique(message.store()); + case protobufs::Transformation::TransformationCase::kSwapCommutableOperands: + return MakeUnique( + message.swap_commutable_operands()); case protobufs::Transformation::TransformationCase::kVectorShuffle: return MakeUnique(message.vector_shuffle()); case protobufs::Transformation::TRANSFORMATION_NOT_SET: diff --git a/source/fuzz/transformation_swap_commutable_operands.cpp b/source/fuzz/transformation_swap_commutable_operands.cpp new file mode 100644 index 0000000000..49d9de8325 --- /dev/null +++ b/source/fuzz/transformation_swap_commutable_operands.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_swap_commutable_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( + const spvtools::fuzz::protobufs::TransformationSwapCommutableOperands& + message) + : message_(message) {} + +TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( + const protobufs::InstructionDescriptor& instruction_descriptor) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationSwapCommutableOperands::IsApplicable( + opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/ + ) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), context); + if (instruction == nullptr) return false; + + SpvOp opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + assert(instruction->opcode() == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + return spvOpcodeIsCommutativeBinaryOperator(opcode); +} + +void TransformationSwapCommutableOperands::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/ + ) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), context); + // By design, the instructions defined to be commutative have exactly two + // input parameters. + std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1)); +} + +protobufs::Transformation TransformationSwapCommutableOperands::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_swap_commutable_operands() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_swap_commutable_operands.h b/source/fuzz/transformation_swap_commutable_operands.h new file mode 100644 index 0000000000..061e92dac8 --- /dev/null +++ b/source/fuzz/transformation_swap_commutable_operands.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_SWAP_COMMUTABLE_OPERANDS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_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 TransformationSwapCommutableOperands : public Transformation { + public: + explicit TransformationSwapCommutableOperands( + const protobufs::TransformationSwapCommutableOperands& message); + + TransformationSwapCommutableOperands( + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.instruction_descriptor| must identify an existing + // commutative instruction + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Swaps the commutable operands. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapCommutableOperands message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ diff --git a/source/opcode.cpp b/source/opcode.cpp index a837b95121..b38b5d4a02 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -612,6 +612,39 @@ bool spvOpcodeIsDebug(SpvOp opcode) { } } +bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode) { + switch (opcode) { + case SpvOpPtrEqual: + case SpvOpPtrNotEqual: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + return true; + default: + return false; + } +} + std::vector spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) { switch (opcode) { case SpvOpMemoryBarrier: diff --git a/source/opcode.h b/source/opcode.h index f79826fcde..b4f02718f8 100644 --- a/source/opcode.h +++ b/source/opcode.h @@ -130,6 +130,10 @@ bool spvOpcodeIsScalarizable(SpvOp opcode); // Returns true if the given opcode is a debug instruction. bool spvOpcodeIsDebug(SpvOp opcode); +// Returns true for opcodes that are binary operators, +// where the order of the operands is irrelevant. +bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode); + // Returns a vector containing the indices of the memory semantics // operands for |opcode|. std::vector spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode); diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 6ec4c2ad4d..40aa152fbf 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -63,6 +63,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_set_selection_control_test.cpp transformation_split_block_test.cpp transformation_store_test.cpp + transformation_swap_commutable_operands_test.cpp transformation_vector_shuffle_test.cpp uniform_buffer_element_descriptor_test.cpp) diff --git a/test/fuzz/transformation_swap_commutable_operands_test.cpp b/test/fuzz/transformation_swap_commutable_operands_test.cpp new file mode 100644 index 0000000000..f0591cf1c2 --- /dev/null +++ b/test/fuzz/transformation_swap_commutable_operands_test.cpp @@ -0,0 +1,427 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_swap_commutable_operands.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSwapCommutableOperandsTest, IsApplicableTest) { + 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 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager factManager; + + // Tests existing commutative instructions + auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0); + auto transformation = + TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + // Tests existing non-commutative instructions + instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(8, SpvOpConstant, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(11, SpvOpVariable, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = + MakeInstructionDescriptor(14, SpvOpConstantComposite, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + // Tests the base instruction id not existing + instructionDescriptor = MakeInstructionDescriptor(67, SpvOpIAddCarry, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(68, SpvOpIEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(69, SpvOpINotEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(70, SpvOpFOrdEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(71, SpvOpPtrEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + // Tests there being no instruction with the desired opcode after the base + // instruction id + instructionDescriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(38, SpvOpIMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(45, SpvOpFAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(66, SpvOpFMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + // Tests there being an instruction with the desired opcode after the base + // instruction id, but the skip count associated with the instruction + // descriptor being so high. + instructionDescriptor = MakeInstructionDescriptor(11, SpvOpIAdd, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(16, SpvOpIMul, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(23, SpvOpFAdd, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(32, SpvOpFMul, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(37, SpvOpDot, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); +} + +TEST(TransformationSwapCommutableOperandsTest, ApplyTest) { + 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 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager factManager; + + auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0); + auto transformation = + TransformationSwapCommutableOperands(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + std::string variantShader = 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 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %21 %19 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %27 %25 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %41 %39 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %47 %45 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %65 %63 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variantShader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/utils/check_copyright.py b/utils/check_copyright.py index 2d288a1226..1e8faf09d3 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# coding=utf-8 # Copyright (c) 2016 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,7 +32,8 @@ 'Google Inc.', 'Google LLC', 'Pierre Moreau', - 'Samsung Inc'] + 'Samsung Inc', + 'André Perez Maselco'] CURRENT_YEAR='2020' YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)' @@ -167,7 +169,7 @@ def alert_if_no_copyright(glob, comment_prefix): has_apache2 = False line_num = 0 apache_expected_end = 0 - with open(file) as contents: + with open(file, encoding='utf-8') as contents: for line in contents: line_num += 1 if COPYRIGHT_RE.search(line): From da4cd214850296225484d3c2575c0e7eec83d607 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Fri, 6 Mar 2020 12:25:57 +0000 Subject: [PATCH 37/88] spirv-fuzz: Use better function name (#3207) Changes FuzzerPass::MaybeAddTransformationBeforeEachInstruction to FuzzerPass::ForEachInstructionWithInstructionDescriptor. Fixes #3184. --- source/fuzz/fuzzer_pass.cpp | 13 ++++++------- source/fuzz/fuzzer_pass.h | 16 ++++++++-------- source/fuzz/fuzzer_pass_add_access_chains.cpp | 2 +- .../fuzzer_pass_add_equation_instructions.cpp | 2 +- source/fuzz/fuzzer_pass_add_function_calls.cpp | 2 +- source/fuzz/fuzzer_pass_add_loads.cpp | 2 +- source/fuzz/fuzzer_pass_add_stores.cpp | 2 +- source/fuzz/fuzzer_pass_construct_composites.cpp | 2 +- source/fuzz/fuzzer_pass_copy_objects.cpp | 2 +- 9 files changed, 21 insertions(+), 22 deletions(-) diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 4a22a211ce..73a0285587 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -89,12 +89,12 @@ std::vector FuzzerPass::FindAvailableInstructions( return result; } -void FuzzerPass::MaybeAddTransformationBeforeEachInstruction( +void FuzzerPass::ForEachInstructionWithInstructionDescriptor( std::function< void(opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor)> - maybe_apply_transformation) { + action) { // Consider every block in every function. for (auto& function : *GetIRContext()->module()) { for (auto& block : function) { @@ -132,11 +132,10 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction( const SpvOp opcode = inst_it->opcode(); // Invoke the provided function, which might apply a transformation. - maybe_apply_transformation( - &function, &block, inst_it, - MakeInstructionDescriptor( - base, opcode, - skip_count.count(opcode) ? skip_count.at(opcode) : 0)); + action(&function, &block, inst_it, + MakeInstructionDescriptor( + base, opcode, + skip_count.count(opcode) ? skip_count.at(opcode) : 0)); if (!inst_it->HasResultId()) { skip_count[opcode] = diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 7052685e9f..910eed1330 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -70,24 +70,24 @@ class FuzzerPass { // all times tracking an instruction descriptor that allows the latest // instruction to be located even if it has no result id. // - // The code to manipulate the instruction descriptor is a bit fiddly, and the + // The code to manipulate the instruction descriptor is a bit fiddly. 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 + // The function |action| is invoked for each instruction |inst_it| in block + // |block| of function |function| that is encountered. The // |instruction_descriptor| parameter to the function object allows |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. - void MaybeAddTransformationBeforeEachInstruction( + // In most intended use cases, the job of |action| is to randomly decide + // whether to try to apply some transformation, and then - if selected - to + // attempt to apply it. + void ForEachInstructionWithInstructionDescriptor( std::function< void(opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor)> - maybe_apply_transformation); + action); // A generic helper for applying a transformation that should be applicable // by construction, and adding it to the sequence of applied transformations. diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp index 11f368e60e..cfc2812b46 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -29,7 +29,7 @@ FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default; void FuzzerPassAddAccessChains::Apply() { - MaybeAddTransformationBeforeEachInstruction( + ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 5ffc31c784..6d3bfa39d9 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -32,7 +32,7 @@ FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() = default; void FuzzerPassAddEquationInstructions::Apply() { - MaybeAddTransformationBeforeEachInstruction( + ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) { diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp index c89ae51641..304f64706a 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -32,7 +32,7 @@ FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default; void FuzzerPassAddFunctionCalls::Apply() { - MaybeAddTransformationBeforeEachInstruction( + ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index 2fe1220e11..851787fedf 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -29,7 +29,7 @@ FuzzerPassAddLoads::FuzzerPassAddLoads( FuzzerPassAddLoads::~FuzzerPassAddLoads() = default; void FuzzerPassAddLoads::Apply() { - MaybeAddTransformationBeforeEachInstruction( + ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp index d2c7b3df2d..794ddc3def 100644 --- a/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -29,7 +29,7 @@ FuzzerPassAddStores::FuzzerPassAddStores( FuzzerPassAddStores::~FuzzerPassAddStores() = default; void FuzzerPassAddStores::Apply() { - MaybeAddTransformationBeforeEachInstruction( + ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index e160302162..330b9cfcfc 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -42,7 +42,7 @@ void FuzzerPassConstructComposites::Apply() { } } - MaybeAddTransformationBeforeEachInstruction( + ForEachInstructionWithInstructionDescriptor( [this, &composite_type_ids]( opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp index 0fbe5cbc8c..588cfb600f 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -29,7 +29,7 @@ FuzzerPassCopyObjects::FuzzerPassCopyObjects( FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default; void FuzzerPassCopyObjects::Apply() { - MaybeAddTransformationBeforeEachInstruction( + ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) From 533af49812c7a36b4e73e1b2860c263dbe78032a Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Sun, 8 Mar 2020 16:27:05 +0200 Subject: [PATCH 38/88] spirv-fuzz: Add fuzzer pass to permute function parameters (#3212) Fixes #3194. --- .gitignore | 3 + source/fuzz/CMakeLists.txt | 4 + source/fuzz/fuzzer.cpp | 4 + source/fuzz/fuzzer_context.cpp | 3 + source/fuzz/fuzzer_context.h | 38 ++ source/fuzz/fuzzer_pass.cpp | 21 + source/fuzz/fuzzer_pass.h | 6 + ...uzzer_pass_permute_function_parameters.cpp | 81 ++++ .../fuzzer_pass_permute_function_parameters.h | 45 ++ source/fuzz/fuzzer_util.cpp | 15 + source/fuzz/fuzzer_util.h | 10 + source/fuzz/protobufs/spvtoolsfuzz.proto | 31 ++ source/fuzz/transformation.cpp | 5 + .../fuzz/transformation_add_type_function.h | 2 +- ...sformation_permute_function_parameters.cpp | 184 ++++++++ ...ansformation_permute_function_parameters.h | 61 +++ test/fuzz/CMakeLists.txt | 1 + ...ation_permute_function_parameters_test.cpp | 430 ++++++++++++++++++ utils/check_copyright.py | 3 +- 19 files changed, 945 insertions(+), 2 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_permute_function_parameters.cpp create mode 100644 source/fuzz/fuzzer_pass_permute_function_parameters.h create mode 100644 source/fuzz/transformation_permute_function_parameters.cpp create mode 100644 source/fuzz/transformation_permute_function_parameters.h create mode 100644 test/fuzz/transformation_permute_function_parameters_test.cpp diff --git a/.gitignore b/.gitignore index 196c63c922..b2af56e92b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ bazel-testlogs # C-Lion /.idea/ /cmake-build-*/ + +# VSCode +/.vscode/* diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 231ea75750..8fd05939c5 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -62,6 +62,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_obfuscate_constants.h fuzzer_pass_outline_functions.h fuzzer_pass_permute_blocks.h + fuzzer_pass_permute_function_parameters.h fuzzer_pass_split_blocks.h fuzzer_pass_swap_commutable_operands.h fuzzer_util.h @@ -104,6 +105,7 @@ if(SPIRV_BUILD_FUZZER) transformation_merge_blocks.h transformation_move_block_down.h transformation_outline_function.h + transformation_permute_function_parameters.h transformation_replace_boolean_constant_with_constant_binary.h transformation_replace_constant_with_uniform.h transformation_replace_id_with_synonym.h @@ -150,6 +152,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_obfuscate_constants.cpp fuzzer_pass_outline_functions.cpp fuzzer_pass_permute_blocks.cpp + fuzzer_pass_permute_function_parameters.cpp fuzzer_pass_split_blocks.cpp fuzzer_pass_swap_commutable_operands.cpp fuzzer_util.cpp @@ -191,6 +194,7 @@ if(SPIRV_BUILD_FUZZER) transformation_merge_blocks.cpp transformation_move_block_down.cpp transformation_outline_function.cpp + transformation_permute_function_parameters.cpp transformation_replace_boolean_constant_with_constant_binary.cpp transformation_replace_constant_with_uniform.cpp transformation_replace_id_with_synonym.cpp diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 945bcc59c8..a89c361ff6 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -45,6 +45,7 @@ #include "source/fuzz/fuzzer_pass_obfuscate_constants.h" #include "source/fuzz/fuzzer_pass_outline_functions.h" #include "source/fuzz/fuzzer_pass_permute_blocks.h" +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" @@ -244,6 +245,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index a374f299e7..dd2d7c3f48 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -60,6 +60,7 @@ const std::pair kChanceOfMergingBlocks = {20, 95}; const std::pair kChanceOfMovingBlockDown = {20, 50}; const std::pair kChanceOfObfuscatingConstant = {10, 90}; const std::pair kChanceOfOutliningFunction = {10, 90}; +const std::pair kChanceOfPermutingParameters = {30, 90}; const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; const std::pair kChanceOfSplittingBlock = {40, 95}; @@ -146,6 +147,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant); chance_of_outlining_function_ = ChooseBetweenMinAndMax(kChanceOfOutliningFunction); + chance_of_permuting_parameters_ = + ChooseBetweenMinAndMax(kChanceOfPermutingParameters); chance_of_replacing_id_with_synonym_ = ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 1d8b7bf2e0..813f232d79 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -62,6 +62,40 @@ class FuzzerContext { return result; } + // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively. + // |lo| and |hi| must be valid indices to the |sequence| + template + void Shuffle(std::vector* sequence, size_t lo, size_t hi) const { + auto& array = *sequence; + + if (array.empty()) { + return; + } + + assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid"); + + // i > lo to account for potential infinite loop when lo == 0 + for (size_t i = hi; i > lo; --i) { + auto index = + random_generator_->RandomUint32(static_cast(i - lo + 1)); + + if (lo + index != i) { + // Introduce std::swap to the scope but don't use it + // directly since there might be a better overload + using std::swap; + swap(array[lo + index], array[i]); + } + } + } + + // Ramdomly shuffles a |sequence| + template + void Shuffle(std::vector* sequence) const { + if (!sequence->empty()) { + Shuffle(sequence, 0, sequence->size() - 1); + } + } + // Yields an id that is guaranteed not to be used in the module being fuzzed, // or to have been issued before. uint32_t GetFreshId(); @@ -139,6 +173,9 @@ class FuzzerContext { uint32_t GetChanceOfOutliningFunction() { return chance_of_outlining_function_; } + uint32_t GetChanceOfPermutingParameters() { + return chance_of_permuting_parameters_; + } uint32_t GetChanceOfReplacingIdWithSynonym() { return chance_of_replacing_id_with_synonym_; } @@ -203,6 +240,7 @@ class FuzzerContext { uint32_t chance_of_moving_block_down_; uint32_t chance_of_obfuscating_constant_; uint32_t chance_of_outlining_function_; + uint32_t chance_of_permuting_parameters_; uint32_t chance_of_replacing_id_with_synonym_; uint32_t chance_of_splitting_block_; diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 73a0285587..a76f10d3a4 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -22,6 +22,7 @@ #include "source/fuzz/transformation_add_global_undef.h" #include "source/fuzz/transformation_add_type_boolean.h" #include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" #include "source/fuzz/transformation_add_type_int.h" #include "source/fuzz/transformation_add_type_matrix.h" #include "source/fuzz/transformation_add_type_pointer.h" @@ -179,6 +180,26 @@ uint32_t FuzzerPass::FindOrCreate32BitFloatType() { return result; } +uint32_t FuzzerPass::FindOrCreateFunctionType( + uint32_t return_type_id, const std::vector& argument_id) { + // FindFunctionType has a sigle argument for OpTypeFunction operands + // so we will have to copy them all in this vector + std::vector type_ids(argument_id.size() + 1); + type_ids[0] = return_type_id; + std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1); + + // Check if type exists + auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids); + if (existing_id) { + return existing_id; + } + + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeFunction(result, return_type_id, argument_id)); + return result; +} + uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id, uint32_t component_count) { assert(component_count >= 2 && component_count <= 4 && diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 910eed1330..46ee4080af 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -112,6 +112,12 @@ class FuzzerPass { // instruction does not exist, a transformation is applied to add it. uint32_t FindOrCreate32BitFloatType(); + // Returns the id of an OpTypeFunction % %<...argument_id> + // instruction. If such an instruction doesn't exist, a transformation + // is applied to create a new one. + uint32_t FindOrCreateFunctionType(uint32_t return_type_id, + const std::vector& argument_id); + // Returns the id of an OpTypeVector instruction, with |component_type_id| // (which must already exist) as its base type, and |component_count| // elements (which must be in the range [2, 4]). If such an instruction does diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp new file mode 100644 index 0000000000..2c49860efc --- /dev/null +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// 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 +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_permute_function_parameters.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() = + default; + +void FuzzerPassPermuteFunctionParameters::Apply() { + for (const auto& function : *GetIRContext()->module()) { + uint32_t function_id = function.result_id(); + + // Skip the function if it is an entry point + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), function_id)) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingParameters())) { + continue; + } + + // Compute permutation for parameters + auto* function_type = + fuzzerutil::GetFunctionType(GetIRContext(), &function); + assert(function_type && "Function type is null"); + + // Don't take return type into account + uint32_t arg_size = function_type->NumInOperands() - 1; + + // Create a vector, fill it with [0, n-1] values and shuffle it + std::vector permutation(arg_size); + std::iota(permutation.begin(), permutation.end(), 0); + GetFuzzerContext()->Shuffle(&permutation); + + // Create a new OpFunctionType instruction with permuted arguments + // if needed + auto result_type_id = function_type->GetSingleWordInOperand(0); + std::vector argument_ids; + + for (auto index : permutation) { + // +1 to take function's return type into account + argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1)); + } + + // Apply our transformation + ApplyTransformation(TransformationPermuteFunctionParameters( + function_id, FindOrCreateFunctionType(result_type_id, argument_ids), + permutation)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h new file mode 100644 index 0000000000..bc79804943 --- /dev/null +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// 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_PERMUTE_FUNCTION_PARAMETERS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that, given a non-entry-point function taking n parameters +// and a permutation of the set [0, n - 1]: +// 1. Introduces a new function type that is the same as the original +// function's type but with the order of arguments permuted +// (only add this if it doesn't already exist) +// 2. Changes the type of the function to this type +// 3. Adjusts all calls to the function so that their arguments are permuted +class FuzzerPassPermuteFunctionParameters : public FuzzerPass { + public: + FuzzerPassPermuteFunctionParameters( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPermuteFunctionParameters(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 26961c8d7e..4bfa195064 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -395,6 +395,12 @@ uint32_t FindFunctionType(opt::IRContext* ir_context, return 0; } +opt::Instruction* GetFunctionType(opt::IRContext* context, + const opt::Function* function) { + uint32_t type_id = function->DefInst().GetSingleWordInOperand(1); + return context->get_def_use_mgr()->GetDef(type_id); +} + opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) { for (auto& function : *ir_context->module()) { if (function.result_id() == function_id) { @@ -404,6 +410,15 @@ opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) { return nullptr; } +bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) { + for (auto& entry_point : context->module()->entry_points()) { + if (entry_point.GetSingleWordInOperand(1) == function_id) { + return true; + } + } + return false; +} + bool IdIsAvailableAtUse(opt::IRContext* context, opt::Instruction* use_instruction, uint32_t use_input_operand_index, uint32_t id) { diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index daa836c1d4..ddc0d5a1ee 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -152,10 +152,20 @@ bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id); uint32_t FindFunctionType(opt::IRContext* ir_context, const std::vector& type_ids); +// Returns a type instruction (OpTypeFunction) for |function|. +// Returns |nullptr| if type is not found. +opt::Instruction* GetFunctionType(opt::IRContext* context, + const opt::Function* function); + // Returns the function with result id |function_id|, or |nullptr| if no such // function exists. opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id); +// Returns |true| if one of entry points has function id |function_id|. +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3218): +// TransformationAddFunctionCall also has this functionality as a static method +bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id); + // Checks whether |id| is available (according to dominance rules) at the use // point defined by input operand |use_input_operand_index| of // |use_instruction|. diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 25f0142039..83dd2cfad0 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -370,6 +370,7 @@ message Transformation { TransformationAccessChain access_chain = 39; TransformationEquationInstruction equation_instruction = 40; TransformationSwapCommutableOperands swap_commutable_operands = 41; + TransformationPermuteFunctionParameters permute_function_parameters = 42; // Add additional option using the next available number. } } @@ -907,6 +908,36 @@ message TransformationOutlineFunction { } +message TransformationPermuteFunctionParameters { + + // A transformation that, given a non-entry-point function taking n + // parameters and a permutation of the set [0, n-1]: + // - Introduces a new function type that is the same as the original + // function's type but with the order of arguments permuted + // (only if it doesn't already exist) + // - Changes the type of the function to this type + // - Adjusts all calls to the function so that their arguments are permuted + + // Function, whose parameters will be permuted + uint32 function_id = 1; + + // |new_type_id| is a result id of a valid OpTypeFunction instruction. + // New type is valid if: + // - it has the same number of operands as the old one + // - function's result type is the same as the old one + // - function's arguments are permuted according to |permutation| vector + uint32 new_type_id = 2; + + // An array of size |n|, where |n| is a number of arguments to a function + // with |function_id|. For each i: 0 <= permutation[i] < n. + // + // i-th element of this array contains a position for an i-th + // function's argument (i.e. i-th argument will be permutation[i]-th + // after running this transformation) + repeated uint32 permutation = 3; + +} + message TransformationReplaceBooleanConstantWithConstantBinary { // A transformation to capture replacing a use of a boolean constant with diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index fe0d41c806..e06999c254 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -47,6 +47,7 @@ #include "source/fuzz/transformation_merge_blocks.h" #include "source/fuzz/transformation_move_block_down.h" #include "source/fuzz/transformation_outline_function.h" +#include "source/fuzz/transformation_permute_function_parameters.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" @@ -144,6 +145,10 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kOutlineFunction: return MakeUnique( message.outline_function()); + case protobufs::Transformation::TransformationCase:: + kPermuteFunctionParameters: + return MakeUnique( + message.permute_function_parameters()); case protobufs::Transformation::TransformationCase:: kReplaceBooleanConstantWithConstantBinary: return MakeUnique( diff --git a/source/fuzz/transformation_add_type_function.h b/source/fuzz/transformation_add_type_function.h index 2b596613ae..3880963d03 100644 --- a/source/fuzz/transformation_add_type_function.h +++ b/source/fuzz/transformation_add_type_function.h @@ -37,7 +37,7 @@ class TransformationAddTypeFunction : public Transformation { // - |message_.return_type_id| and each element of |message_.argument_type_id| // must be the ids of non-function types // - The module must not contain an OpTypeFunction instruction defining a - // function type with the signature provided by teh given return and + // function type with the signature provided by the given return and // argument types bool IsApplicable(opt::IRContext* context, const FactManager& fact_manager) const override; diff --git a/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp new file mode 100644 index 0000000000..2141533551 --- /dev/null +++ b/source/fuzz/transformation_permute_function_parameters.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// 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 +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_permute_function_parameters.h" + +namespace spvtools { +namespace fuzz { + +TransformationPermuteFunctionParameters:: + TransformationPermuteFunctionParameters( + const spvtools::fuzz::protobufs:: + TransformationPermuteFunctionParameters& message) + : message_(message) {} + +TransformationPermuteFunctionParameters:: + TransformationPermuteFunctionParameters( + uint32_t function_id, uint32_t new_type_id, + const std::vector& permutation) { + message_.set_function_id(function_id); + message_.set_new_type_id(new_type_id); + + for (auto index : permutation) { + message_.add_permutation(index); + } +} + +bool TransformationPermuteFunctionParameters::IsApplicable( + opt::IRContext* context, const FactManager& /*unused*/) const { + // Check that function exists + const auto* function = + fuzzerutil::FindFunction(context, message_.function_id()); + if (!function || function->DefInst().opcode() != SpvOpFunction || + fuzzerutil::FunctionIsEntryPoint(context, function->result_id())) { + return false; + } + + // Check that permutation has valid indices + const auto* function_type = fuzzerutil::GetFunctionType(context, function); + assert(function_type && "Function type is null"); + + const auto& permutation = message_.permutation(); + + // Don't take return type into account + auto arg_size = function_type->NumInOperands() - 1; + + // |permutation| vector should be equal to the number of arguments + if (static_cast(permutation.size()) != arg_size) { + return false; + } + + // Check that all indices are valid + // and unique integers from the [0, n-1] set + std::unordered_set unique_indices; + for (auto index : permutation) { + // We don't compare |index| with 0 since it's an unsigned integer + if (index >= arg_size) { + return false; + } + + unique_indices.insert(index); + } + + // Check that permutation doesn't have duplicated values + assert(unique_indices.size() == arg_size && "Permutation has duplicates"); + + // Check that new function's type is valid: + // - Has the same number of operands + // - Has the same result type as the old one + // - Order of arguments is permuted + auto new_type_id = message_.new_type_id(); + const auto* new_type = context->get_def_use_mgr()->GetDef(new_type_id); + + if (!new_type || new_type->opcode() != SpvOpTypeFunction || + new_type->NumInOperands() != function_type->NumInOperands()) { + return false; + } + + // Check that both instructions have the same result type + if (new_type->GetSingleWordInOperand(0) != + function_type->GetSingleWordInOperand(0)) { + return false; + } + + // Check that new function type has its arguments permuted + for (int i = 0, n = static_cast(permutation.size()); i < n; ++i) { + // +1 to take return type into account + if (new_type->GetSingleWordInOperand(i + 1) != + function_type->GetSingleWordInOperand(permutation[i] + 1)) { + return false; + } + } + + return true; +} + +void TransformationPermuteFunctionParameters::Apply( + opt::IRContext* context, FactManager* /*unused*/) const { + // Retrieve all data from the message + uint32_t function_id = message_.function_id(); + uint32_t new_type_id = message_.new_type_id(); + const auto& permutation = message_.permutation(); + + // Find the function that will be transformed + auto* function = fuzzerutil::FindFunction(context, function_id); + assert(function && "Can't find the function"); + + // Change function's type + function->DefInst().SetInOperand(1, {new_type_id}); + + // Adjust OpFunctionParameter instructions + + // Collect ids and types from OpFunctionParameter instructions + std::vector param_id, param_type; + function->ForEachParam( + [¶m_id, ¶m_type](const opt::Instruction* param) { + param_id.push_back(param->result_id()); + param_type.push_back(param->type_id()); + }); + + // Permute parameters' ids and types + std::vector permuted_param_id, permuted_param_type; + for (auto index : permutation) { + permuted_param_id.push_back(param_id[index]); + permuted_param_type.push_back(param_type[index]); + } + + // Set OpFunctionParameter instructions to point to new parameters + size_t i = 0; + function->ForEachParam( + [&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) { + param->SetResultType(permuted_param_type[i]); + param->SetResultId(permuted_param_id[i]); + ++i; + }); + + // Fix all OpFunctionCall instructions + context->get_def_use_mgr()->ForEachUser( + &function->DefInst(), + [function_id, &permutation](opt::Instruction* call) { + if (call->opcode() != SpvOpFunctionCall || + call->GetSingleWordInOperand(0) != function_id) { + return; + } + + opt::Instruction::OperandList call_operands = { + call->GetInOperand(0) // Function id + }; + + for (auto index : permutation) { + // Take function id into account + call_operands.push_back(call->GetInOperand(index + 1)); + } + + call->SetInOperands(std::move(call_operands)); + }); + + // Make sure our changes are analyzed + context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_permute_function_parameters() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_permute_function_parameters.h b/source/fuzz/transformation_permute_function_parameters.h new file mode 100644 index 0000000000..c67a735697 --- /dev/null +++ b/source/fuzz/transformation_permute_function_parameters.h @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// 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_PERMUTE_FUNCTION_PARAMETERS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_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 TransformationPermuteFunctionParameters : public Transformation { + public: + explicit TransformationPermuteFunctionParameters( + const protobufs::TransformationPermuteFunctionParameters& message); + + TransformationPermuteFunctionParameters( + uint32_t function_id, uint32_t new_type_id, + const std::vector& permutation); + + // - |function_id| is a valid non-entry-point OpFunction instruction + // - |new_type_id| is a result id of a valid OpTypeFunction instruction. + // New type is valid if: + // - it has the same number of operands as the old one + // - function's result type is the same as the old one + // - function's arguments are permuted according to |permutation| vector + // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments + // to the function + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // - OpFunction instruction with |result_id == function_id| is changed. + // Its arguments are permuted according to the |permutation| vector + // - Changed function gets a new type specified by |type_id| + // - Calls to the function are adjusted accordingly + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationPermuteFunctionParameters message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 40aa152fbf..1e26e74136 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -54,6 +54,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_merge_blocks_test.cpp transformation_move_block_down_test.cpp transformation_outline_function_test.cpp + transformation_permute_function_parameters_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 diff --git a/test/fuzz/transformation_permute_function_parameters_test.cpp b/test/fuzz/transformation_permute_function_parameters_test.cpp new file mode 100644 index 0000000000..1af4699cda --- /dev/null +++ b/test/fuzz/transformation_permute_function_parameters_test.cpp @@ -0,0 +1,430 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// 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_permute_function_parameters.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationPermuteFunctionParametersTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %72 %74 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "g(f1;f1;" + OpName %10 "x" + OpName %11 "y" + OpName %22 "f(f1;i1;vf2;" + OpName %19 "x" + OpName %20 "y" + OpName %21 "z" + OpName %28 "cond(i1;f1;" + OpName %26 "a" + OpName %27 "b" + OpName %53 "param" + OpName %54 "param" + OpName %66 "param" + OpName %67 "param" + OpName %72 "color" + OpName %74 "gl_FragCoord" + OpName %75 "param" + OpName %79 "param" + OpName %85 "param" + OpName %86 "param" + OpName %91 "param" + OpName %92 "param" + OpName %93 "param" + OpName %99 "param" + OpName %100 "param" + OpName %101 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %72 Location 0 + OpDecorate %74 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeVector %6 4 + %9 = OpTypeFunction %8 %7 %7 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %16 = OpTypeVector %6 2 + %17 = OpTypePointer Function %16 + %18 = OpTypeFunction %8 %7 %15 %17 + %24 = OpTypeBool + %25 = OpTypeFunction %24 %15 %7 + %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28 + %31 = OpConstant %6 255 + %33 = OpConstant %6 0 + %34 = OpConstant %6 1 + %42 = OpTypeInt 32 0 + %43 = OpConstant %42 0 + %49 = OpConstant %42 1 + %64 = OpConstant %14 4 + %65 = OpConstant %6 5 + %71 = OpTypePointer Output %8 + %72 = OpVariable %71 Output + %73 = OpTypePointer Input %8 + %74 = OpVariable %73 Input + %76 = OpTypePointer Input %6 + %84 = OpConstant %14 5 + %90 = OpConstant %6 3 + %98 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %66 = OpVariable %15 Function + %67 = OpVariable %7 Function + %75 = OpVariable %7 Function + %79 = OpVariable %7 Function + %85 = OpVariable %15 Function + %86 = OpVariable %7 Function + %91 = OpVariable %7 Function + %92 = OpVariable %15 Function + %93 = OpVariable %17 Function + %99 = OpVariable %7 Function + %100 = OpVariable %15 Function + %101 = OpVariable %17 Function + OpStore %66 %64 + OpStore %67 %65 + %68 = OpFunctionCall %24 %28 %66 %67 + OpSelectionMerge %70 None + OpBranchConditional %68 %69 %83 + %69 = OpLabel + %77 = OpAccessChain %76 %74 %43 + %78 = OpLoad %6 %77 + OpStore %75 %78 + %80 = OpAccessChain %76 %74 %49 + %81 = OpLoad %6 %80 + OpStore %79 %81 + %82 = OpFunctionCall %8 %12 %75 %79 + OpStore %72 %82 + OpBranch %70 + %83 = OpLabel + OpStore %85 %84 + OpStore %86 %65 + %87 = OpFunctionCall %24 %28 %85 %86 + OpSelectionMerge %89 None + OpBranchConditional %87 %88 %97 + %88 = OpLabel + OpStore %91 %90 + OpStore %92 %64 + %94 = OpLoad %8 %74 + %95 = OpVectorShuffle %16 %94 %94 0 1 + OpStore %93 %95 + %96 = OpFunctionCall %8 %22 %91 %92 %93 + OpStore %72 %96 + OpBranch %89 + %97 = OpLabel + OpStore %99 %98 + OpStore %100 %84 + %102 = OpLoad %8 %74 + %103 = OpVectorShuffle %16 %102 %102 0 1 + OpStore %101 %103 + %104 = OpFunctionCall %8 %22 %99 %100 %101 + OpStore %72 %104 + OpBranch %89 + %89 = OpLabel + OpBranch %70 + %70 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %9 + %10 = OpFunctionParameter %7 + %11 = OpFunctionParameter %7 + %13 = OpLabel + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + %35 = OpLoad %6 %11 + %36 = OpFDiv %6 %35 %31 + %37 = OpFSub %6 %34 %36 + %38 = OpCompositeConstruct %8 %32 %33 %37 %34 + OpReturnValue %38 + OpFunctionEnd + %22 = OpFunction %8 None %18 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %15 + %21 = OpFunctionParameter %17 + %23 = OpLabel + %53 = OpVariable %7 Function + %54 = OpVariable %7 Function + %41 = OpLoad %6 %19 + %44 = OpAccessChain %7 %21 %43 + %45 = OpLoad %6 %44 + %46 = OpFAdd %6 %41 %45 + %47 = OpLoad %14 %20 + %48 = OpConvertSToF %6 %47 + %50 = OpAccessChain %7 %21 %49 + %51 = OpLoad %6 %50 + %52 = OpFAdd %6 %48 %51 + OpStore %53 %46 + OpStore %54 %52 + %55 = OpFunctionCall %8 %12 %53 %54 + OpReturnValue %55 + OpFunctionEnd + %28 = OpFunction %24 None %25 + %26 = OpFunctionParameter %15 + %27 = OpFunctionParameter %7 + %29 = OpLabel + %58 = OpLoad %14 %26 + %59 = OpConvertSToF %6 %58 + %60 = OpLoad %6 %27 + %61 = OpFOrdLessThan %24 %59 %60 + OpReturnValue %61 + 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; + + // Can't permute main function + ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable( + context.get(), fact_manager)); + + // Can't permute invalid instruction + ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {}) + .IsApplicable(context.get(), fact_manager)); + + // Permutation has too many values + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3}) + .IsApplicable(context.get(), fact_manager)); + + // Permutation has too few values + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1}) + .IsApplicable(context.get(), fact_manager)); + + // Permutation has invalid values + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0}) + .IsApplicable(context.get(), fact_manager)); + + // Type id is not an OpTypeFunction instruction + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0}) + .IsApplicable(context.get(), fact_manager)); + + // Type id has incorrect number of operands + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0}) + .IsApplicable(context.get(), fact_manager)); + + // OpTypeFunction has operands out of order + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0}) + .IsApplicable(context.get(), fact_manager)); + + // Successful transformations + { + // Function has two operands of the same type: + // initial OpTypeFunction should be enough + TransformationPermuteFunctionParameters transformation(12, 9, {1, 0}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + { + TransformationPermuteFunctionParameters transformation(28, 105, {1, 0}); + 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" %72 %74 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "g(f1;f1;" + OpName %10 "x" + OpName %11 "y" + OpName %22 "f(f1;i1;vf2;" + OpName %19 "x" + OpName %20 "y" + OpName %21 "z" + OpName %28 "cond(i1;f1;" + OpName %26 "a" + OpName %27 "b" + OpName %53 "param" + OpName %54 "param" + OpName %66 "param" + OpName %67 "param" + OpName %72 "color" + OpName %74 "gl_FragCoord" + OpName %75 "param" + OpName %79 "param" + OpName %85 "param" + OpName %86 "param" + OpName %91 "param" + OpName %92 "param" + OpName %93 "param" + OpName %99 "param" + OpName %100 "param" + OpName %101 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %72 Location 0 + OpDecorate %74 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeVector %6 4 + %9 = OpTypeFunction %8 %7 %7 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %16 = OpTypeVector %6 2 + %17 = OpTypePointer Function %16 + %18 = OpTypeFunction %8 %7 %15 %17 + %24 = OpTypeBool + %25 = OpTypeFunction %24 %15 %7 + %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28 + %31 = OpConstant %6 255 + %33 = OpConstant %6 0 + %34 = OpConstant %6 1 + %42 = OpTypeInt 32 0 + %43 = OpConstant %42 0 + %49 = OpConstant %42 1 + %64 = OpConstant %14 4 + %65 = OpConstant %6 5 + %71 = OpTypePointer Output %8 + %72 = OpVariable %71 Output + %73 = OpTypePointer Input %8 + %74 = OpVariable %73 Input + %76 = OpTypePointer Input %6 + %84 = OpConstant %14 5 + %90 = OpConstant %6 3 + %98 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %66 = OpVariable %15 Function + %67 = OpVariable %7 Function + %75 = OpVariable %7 Function + %79 = OpVariable %7 Function + %85 = OpVariable %15 Function + %86 = OpVariable %7 Function + %91 = OpVariable %7 Function + %92 = OpVariable %15 Function + %93 = OpVariable %17 Function + %99 = OpVariable %7 Function + %100 = OpVariable %15 Function + %101 = OpVariable %17 Function + OpStore %66 %64 + OpStore %67 %65 + %68 = OpFunctionCall %24 %28 %67 %66 + OpSelectionMerge %70 None + OpBranchConditional %68 %69 %83 + %69 = OpLabel + %77 = OpAccessChain %76 %74 %43 + %78 = OpLoad %6 %77 + OpStore %75 %78 + %80 = OpAccessChain %76 %74 %49 + %81 = OpLoad %6 %80 + OpStore %79 %81 + %82 = OpFunctionCall %8 %12 %79 %75 + OpStore %72 %82 + OpBranch %70 + %83 = OpLabel + OpStore %85 %84 + OpStore %86 %65 + %87 = OpFunctionCall %24 %28 %86 %85 + OpSelectionMerge %89 None + OpBranchConditional %87 %88 %97 + %88 = OpLabel + OpStore %91 %90 + OpStore %92 %64 + %94 = OpLoad %8 %74 + %95 = OpVectorShuffle %16 %94 %94 0 1 + OpStore %93 %95 + %96 = OpFunctionCall %8 %22 %91 %92 %93 + OpStore %72 %96 + OpBranch %89 + %97 = OpLabel + OpStore %99 %98 + OpStore %100 %84 + %102 = OpLoad %8 %74 + %103 = OpVectorShuffle %16 %102 %102 0 1 + OpStore %101 %103 + %104 = OpFunctionCall %8 %22 %99 %100 %101 + OpStore %72 %104 + OpBranch %89 + %89 = OpLabel + OpBranch %70 + %70 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %9 + %11 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %13 = OpLabel + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + %35 = OpLoad %6 %11 + %36 = OpFDiv %6 %35 %31 + %37 = OpFSub %6 %34 %36 + %38 = OpCompositeConstruct %8 %32 %33 %37 %34 + OpReturnValue %38 + OpFunctionEnd + %22 = OpFunction %8 None %18 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %15 + %21 = OpFunctionParameter %17 + %23 = OpLabel + %53 = OpVariable %7 Function + %54 = OpVariable %7 Function + %41 = OpLoad %6 %19 + %44 = OpAccessChain %7 %21 %43 + %45 = OpLoad %6 %44 + %46 = OpFAdd %6 %41 %45 + %47 = OpLoad %14 %20 + %48 = OpConvertSToF %6 %47 + %50 = OpAccessChain %7 %21 %49 + %51 = OpLoad %6 %50 + %52 = OpFAdd %6 %48 %51 + OpStore %53 %46 + OpStore %54 %52 + %55 = OpFunctionCall %8 %12 %54 %53 + OpReturnValue %55 + OpFunctionEnd + %28 = OpFunction %24 None %105 + %27 = OpFunctionParameter %7 + %26 = OpFunctionParameter %15 + %29 = OpLabel + %58 = OpLoad %14 %26 + %59 = OpConvertSToF %6 %58 + %60 = OpLoad %6 %27 + %61 = OpFOrdLessThan %24 %59 %60 + OpReturnValue %61 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/utils/check_copyright.py b/utils/check_copyright.py index 1e8faf09d3..b2283009de 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -33,7 +33,8 @@ 'Google LLC', 'Pierre Moreau', 'Samsung Inc', - 'André Perez Maselco'] + 'André Perez Maselco', + 'Vasyl Teliman'] CURRENT_YEAR='2020' YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)' From 4c027048d88bcdd047ddace38081b6d6f896aba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Perez=20Maselco?= Date: Sun, 8 Mar 2020 19:33:24 -0300 Subject: [PATCH 39/88] spirv-fuzz: Add toggle access chain instruction transformation (#3211) In this PR, the classes that represent the toggle access chain instruction transformation and fuzzer pass were implemented. This transformation toggles the instructions OpAccessChain and OpInBoundsAccessChain between them. Fixes #3193. --- source/fuzz/CMakeLists.txt | 4 + source/fuzz/fuzzer.cpp | 4 + source/fuzz/fuzzer_context.cpp | 4 + source/fuzz/fuzzer_context.h | 4 + ...r_pass_toggle_access_chain_instruction.cpp | 54 +++ ...zer_pass_toggle_access_chain_instruction.h | 40 ++ source/fuzz/protobufs/spvtoolsfuzz.proto | 10 + source/fuzz/transformation.cpp | 5 + ...mation_toggle_access_chain_instruction.cpp | 83 ++++ ...ormation_toggle_access_chain_instruction.h | 51 +++ test/fuzz/CMakeLists.txt | 1 + ...n_toggle_access_chain_instruction_test.cpp | 413 ++++++++++++++++++ 12 files changed, 673 insertions(+) create mode 100644 source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp create mode 100644 source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h create mode 100644 source/fuzz/transformation_toggle_access_chain_instruction.cpp create mode 100644 source/fuzz/transformation_toggle_access_chain_instruction.h create mode 100644 test/fuzz/transformation_toggle_access_chain_instruction_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 8fd05939c5..3a9d604c84 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -65,6 +65,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_permute_function_parameters.h fuzzer_pass_split_blocks.h fuzzer_pass_swap_commutable_operands.h + fuzzer_pass_toggle_access_chain_instruction.h fuzzer_util.h id_use_descriptor.h instruction_descriptor.h @@ -116,6 +117,7 @@ if(SPIRV_BUILD_FUZZER) transformation_split_block.h transformation_store.h transformation_swap_commutable_operands.h + transformation_toggle_access_chain_instruction.h transformation_vector_shuffle.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -155,6 +157,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_permute_function_parameters.cpp fuzzer_pass_split_blocks.cpp fuzzer_pass_swap_commutable_operands.cpp + fuzzer_pass_toggle_access_chain_instruction.cpp fuzzer_util.cpp id_use_descriptor.cpp instruction_descriptor.cpp @@ -205,6 +208,7 @@ if(SPIRV_BUILD_FUZZER) transformation_split_block.cpp transformation_store.cpp transformation_swap_commutable_operands.cpp + transformation_toggle_access_chain_instruction.cpp transformation_vector_shuffle.cpp uniform_buffer_element_descriptor.cpp ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index a89c361ff6..119bd3c329 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -48,6 +48,7 @@ #include "source/fuzz/fuzzer_pass_permute_function_parameters.h" #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" +#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/pseudo_random_generator.h" #include "source/opt/build_module.h" @@ -288,6 +289,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); for (auto& pass : final_passes) { if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) { return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index dd2d7c3f48..2f9fc5af6a 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -63,6 +63,8 @@ const std::pair kChanceOfOutliningFunction = {10, 90}; const std::pair kChanceOfPermutingParameters = {30, 90}; const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; const std::pair kChanceOfSplittingBlock = {40, 95}; +const std::pair kChanceOfTogglingAccessChainInstruction = { + 20, 90}; // Default limits for various quantities that are chosen during fuzzing. // Keep them in alphabetical order. @@ -152,6 +154,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, chance_of_replacing_id_with_synonym_ = ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); + chance_of_toggling_access_chain_instruction_ = + ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction); } FuzzerContext::~FuzzerContext() = default; diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 813f232d79..1529705770 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -180,6 +180,9 @@ class FuzzerContext { return chance_of_replacing_id_with_synonym_; } uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; } + uint32_t GetChanceOfTogglingAccessChainInstruction() { + return chance_of_toggling_access_chain_instruction_; + } uint32_t GetRandomLoopControlPeelCount() { return random_generator_->RandomUint32(max_loop_control_peel_count_); } @@ -243,6 +246,7 @@ class FuzzerContext { uint32_t chance_of_permuting_parameters_; uint32_t chance_of_replacing_id_with_synonym_; uint32_t chance_of_splitting_block_; + uint32_t chance_of_toggling_access_chain_instruction_; // Limits associated with various quantities for which random values are // chosen during fuzzing. diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp new file mode 100644 index 0000000000..9fb175ba2b --- /dev/null +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_toggle_access_chain_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassToggleAccessChainInstruction:: + ~FuzzerPassToggleAccessChainInstruction() = default; + +void FuzzerPassToggleAccessChainInstruction::Apply() { + auto context = GetIRContext(); + // Iterates over the module's instructions and checks whether it is + // OpAccessChain or OpInBoundsAccessChain. In this case, the transformation is + // probabilistically applied. + context->module()->ForEachInst([this, + context](opt::Instruction* instruction) { + SpvOp opcode = instruction->opcode(); + if ((opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) { + auto instructionDescriptor = + MakeInstructionDescriptor(context, instruction); + auto transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyTransformation(transformation); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h new file mode 100644 index 0000000000..ec8c3f78f9 --- /dev/null +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ +#define SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for all access chain instructions in the module, +// probabilistically choosing which of these instructions will be toggled. +class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { + public: + FuzzerPassToggleAccessChainInstruction( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassToggleAccessChainInstruction(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 83dd2cfad0..b816e3b0ef 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -371,6 +371,7 @@ message Transformation { TransformationEquationInstruction equation_instruction = 40; TransformationSwapCommutableOperands swap_commutable_operands = 41; TransformationPermuteFunctionParameters permute_function_parameters = 42; + TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43; // Add additional option using the next available number. } } @@ -1109,6 +1110,15 @@ message TransformationSwapCommutableOperands { } +message TransformationToggleAccessChainInstruction { + + // A transformation that toggles an access chain instruction. + + // A descriptor for an access chain instruction + InstructionDescriptor instruction_descriptor = 1; + +} + message TransformationVectorShuffle { // A transformation that adds a vector shuffle instruction. diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index e06999c254..f18c86bddf 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -58,6 +58,7 @@ #include "source/fuzz/transformation_split_block.h" #include "source/fuzz/transformation_store.h" #include "source/fuzz/transformation_swap_commutable_operands.h" +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" #include "source/fuzz/transformation_vector_shuffle.h" #include "source/util/make_unique.h" @@ -179,6 +180,10 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kSwapCommutableOperands: return MakeUnique( message.swap_commutable_operands()); + case protobufs::Transformation::TransformationCase:: + kToggleAccessChainInstruction: + return MakeUnique( + message.toggle_access_chain_instruction()); case protobufs::Transformation::TransformationCase::kVectorShuffle: return MakeUnique(message.vector_shuffle()); case protobufs::Transformation::TRANSFORMATION_NOT_SET: diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/source/fuzz/transformation_toggle_access_chain_instruction.cpp new file mode 100644 index 0000000000..ace331aa28 --- /dev/null +++ b/source/fuzz/transformation_toggle_access_chain_instruction.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_toggle_access_chain_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationToggleAccessChainInstruction:: + TransformationToggleAccessChainInstruction( + const spvtools::fuzz::protobufs:: + TransformationToggleAccessChainInstruction& message) + : message_(message) {} + +TransformationToggleAccessChainInstruction:: + TransformationToggleAccessChainInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationToggleAccessChainInstruction::IsApplicable( + opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/ + ) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), context); + if (instruction == nullptr) { + return false; + } + + SpvOp opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + + assert(instruction->opcode() == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + + if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) { + return true; + } + + return false; +} + +void TransformationToggleAccessChainInstruction::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/ + ) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), context); + SpvOp opcode = instruction->opcode(); + + if (opcode == SpvOpAccessChain) { + instruction->SetOpcode(SpvOpInBoundsAccessChain); + } else { + assert(opcode == SpvOpInBoundsAccessChain && + "The located instruction must be an OpInBoundsAccessChain " + "instruction."); + instruction->SetOpcode(SpvOpAccessChain); + } +} + +protobufs::Transformation +TransformationToggleAccessChainInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_toggle_access_chain_instruction() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.h b/source/fuzz/transformation_toggle_access_chain_instruction.h new file mode 100644 index 0000000000..125e1ab038 --- /dev/null +++ b/source/fuzz/transformation_toggle_access_chain_instruction.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_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 TransformationToggleAccessChainInstruction : public Transformation { + public: + explicit TransformationToggleAccessChainInstruction( + const protobufs::TransformationToggleAccessChainInstruction& message); + + TransformationToggleAccessChainInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.instruction_descriptor| must identify an existing + // access chain instruction + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Toggles the access chain instruction. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationToggleAccessChainInstruction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 1e26e74136..99a78fd145 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -65,6 +65,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_split_block_test.cpp transformation_store_test.cpp transformation_swap_commutable_operands_test.cpp + transformation_toggle_access_chain_instruction_test.cpp transformation_vector_shuffle_test.cpp uniform_buffer_element_descriptor_test.cpp) diff --git a/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp new file mode 100644 index 0000000000..98e0a6442e --- /dev/null +++ b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp @@ -0,0 +1,413 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_toggle_access_chain_instruction.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationToggleAccessChainInstructionTest, IsApplicableTest) { + 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 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpInBoundsAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpInBoundsAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager factManager; + + // Tests existing access chain instructions + auto instructionDescriptor = + MakeInstructionDescriptor(18, SpvOpAccessChain, 0); + auto transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = + MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = + MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + + // Tests existing non-access chain instructions + instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = + MakeInstructionDescriptor(14, SpvOpConstantComposite, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + // Tests the base instruction id not existing + instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = + MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + // Tests there being no instruction with the desired opcode after the base + // instruction id + instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = + MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + // Tests there being an instruction with the desired opcode after the base + // instruction id, but the skip count associated with the instruction + // descriptor being so high. + instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + + instructionDescriptor = + MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); +} + +TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) { + 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 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpInBoundsAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpInBoundsAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager factManager; + + auto instructionDescriptor = + MakeInstructionDescriptor(18, SpvOpAccessChain, 0); + auto transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = + MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = + MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + transformation.Apply(context.get(), &factManager); + + std::string variantShader = 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 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpInBoundsAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpInBoundsAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpInBoundsAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variantShader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools From 659470446cb88ba4a0384280dc3a119a0d4a5d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Perez=20Maselco?= Date: Mon, 9 Mar 2020 13:16:18 -0300 Subject: [PATCH 40/88] spirv-fuzz: Allow OpPhi operand to be replaced with a composite synonym (#3221) In this PR, the class FuzzerPassApplyIdSynonyms was updated to allow OpPhi operand to be replaced with a composite synonym. Fixes #3209. --- source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index e7e9976700..5711f35885 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -81,9 +81,8 @@ void FuzzerPassApplyIdSynonyms::Apply() { synonyms_to_try.push_back(data_descriptor); } while (!synonyms_to_try.empty()) { - auto synonym_index = GetFuzzerContext()->RandomIndex(synonyms_to_try); - auto synonym_to_try = synonyms_to_try[synonym_index]; - synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index); + auto synonym_to_try = + GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try); // If the synonym's |index_size| is zero, the synonym represents an id. // Otherwise it represents some element of a composite structure, in @@ -91,12 +90,10 @@ void FuzzerPassApplyIdSynonyms::Apply() { // that element out. if (synonym_to_try->index_size() > 0 && !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, - use_inst)) { + use_inst) && + use_inst->opcode() != SpvOpPhi) { // We cannot insert an extract before this instruction, so this // synonym is no good. - // - // TODO(afd): In the case of an OpPhi, we could consider inserting the - // extract instruction into the relevant parent block of the OpPhi. continue; } @@ -115,8 +112,26 @@ void FuzzerPassApplyIdSynonyms::Apply() { id_with_which_to_replace_use = synonym_to_try->object(); } else { id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId(); + opt::Instruction* instruction_to_insert_before = nullptr; + + if (use_inst->opcode() != SpvOpPhi) { + instruction_to_insert_before = use_inst; + } else { + auto parent_block_id = + use_inst->GetSingleWordInOperand(use_in_operand_index + 1); + auto parent_block_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id); + auto parent_block = + GetIRContext()->get_instr_block(parent_block_instruction); + + instruction_to_insert_before = parent_block->GetMergeInst() + ? parent_block->GetMergeInst() + : parent_block->terminator(); + } + ApplyTransformation(TransformationCompositeExtract( - MakeInstructionDescriptor(GetIRContext(), use_inst), + MakeInstructionDescriptor(GetIRContext(), + instruction_to_insert_before), id_with_which_to_replace_use, synonym_to_try->object(), fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()))); } From 7c3de218f495cfbc4416fc762df55f9ffaa3910f Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Mon, 9 Mar 2020 18:17:43 +0200 Subject: [PATCH 41/88] spirv-fuzz: Remove duplicated functionality (#3220) Fixes #3218. --- source/fuzz/fuzzer_pass_add_function_calls.cpp | 4 ++-- source/fuzz/fuzzer_util.h | 2 -- source/fuzz/transformation_function_call.cpp | 12 +----------- source/fuzz/transformation_function_call.h | 4 ---- 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp index 304f64706a..545aa169ff 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -62,8 +62,8 @@ void FuzzerPassAddFunctionCalls::Apply() { std::vector candidate_functions; for (auto& other_function : *GetIRContext()->module()) { if (&other_function != function && - !TransformationFunctionCall::FunctionIsEntryPoint( - GetIRContext(), other_function.result_id())) { + !fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + other_function.result_id())) { candidate_functions.push_back(&other_function); } } diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index ddc0d5a1ee..7be0d59eb6 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -162,8 +162,6 @@ opt::Instruction* GetFunctionType(opt::IRContext* context, opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id); // Returns |true| if one of entry points has function id |function_id|. -// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3218): -// TransformationAddFunctionCall also has this functionality as a static method bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id); // Checks whether |id| is available (according to dominance rules) at the use diff --git a/source/fuzz/transformation_function_call.cpp b/source/fuzz/transformation_function_call.cpp index 69886640dc..cea8537157 100644 --- a/source/fuzz/transformation_function_call.cpp +++ b/source/fuzz/transformation_function_call.cpp @@ -53,7 +53,7 @@ bool TransformationFunctionCall::IsApplicable( } // The function must not be an entry point - if (FunctionIsEntryPoint(context, message_.callee_id())) { + if (fuzzerutil::FunctionIsEntryPoint(context, message_.callee_id())) { return false; } @@ -181,15 +181,5 @@ protobufs::Transformation TransformationFunctionCall::ToMessage() const { return result; } -bool TransformationFunctionCall::FunctionIsEntryPoint(opt::IRContext* context, - uint32_t function_id) { - for (auto& entry_point : context->module()->entry_points()) { - if (entry_point.GetSingleWordInOperand(1) == function_id) { - return true; - } - } - return false; -} - } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/transformation_function_call.h b/source/fuzz/transformation_function_call.h index e977e1dd56..a9ae5bee3c 100644 --- a/source/fuzz/transformation_function_call.h +++ b/source/fuzz/transformation_function_call.h @@ -55,10 +55,6 @@ class TransformationFunctionCall : public Transformation { protobufs::Transformation ToMessage() const override; - // Helper to determine whether |function_id| is targeted by OpEntryPoint. - static bool FunctionIsEntryPoint(opt::IRContext* context, - uint32_t function_id); - private: protobufs::TransformationFunctionCall message_; }; From dd3d91691f1e1dc4c0f42818756cf5e165c8918c Mon Sep 17 00:00:00 2001 From: Jeff Bolz Date: Mon, 9 Mar 2020 14:03:39 -0500 Subject: [PATCH 42/88] Allow sampledimage types as operand of OpCopyObject (#3222) --- source/val/validate_image.cpp | 1 + test/val/val_id_test.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index ed960d1639..5b77058c89 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -847,6 +847,7 @@ bool IsAllowedSampledImageOperand(SpvOp opcode) { case SpvOpImageSparseSampleDrefExplicitLod: case SpvOpImageSparseGather: case SpvOpImageSparseDrefGather: + case SpvOpCopyObject: return true; default: return false; diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index 019d91ad71..adea5639b2 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -4355,6 +4355,17 @@ OpFunctionEnd)"; "'23' as an operand of '24'.")); } +TEST_F(ValidateIdWithMessage, OpCopyObjectSampledImageGood) { + std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( +%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst +%smpld_img2 = OpCopyObject %sampled_image_type %smpld_img +%image_inst2 = OpCopyObject %image_type %image_inst +OpReturn +OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + // Valid: Get a float in a matrix using CompositeExtract. // Valid: Insert float into a matrix using CompositeInsert. TEST_F(ValidateIdWithMessage, CompositeExtractInsertGood) { From 1af1df3b23cf7439ead64c95220c3616d81a3713 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 12 Mar 2020 10:56:11 +0000 Subject: [PATCH 43/88] spirv-fuzz: Fix vector width issue in 'add equation instructions' pass (#3223) Fixes #3213. --- .../fuzzer_pass_add_equation_instructions.cpp | 9 ++-- test/fuzz/fuzzer_replayer_test.cpp | 49 ++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 6d3bfa39d9..7f34344cc3 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -201,9 +201,12 @@ FuzzerPassAddEquationInstructions::RestrictToVectorWidth( std::vector result; for (auto& inst : instructions) { auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); - if ((vector_width == 1 && !type->AsVector()) || - (vector_width > 1 && - type->AsVector()->element_count() == vector_width)) { + // Get the vector width of |inst|, which is 1 if |inst| is a scalar and is + // otherwise derived from its vector type. + uint32_t other_vector_width = + type->AsVector() ? type->AsVector()->element_count() : 1; + // Keep |inst| if the vector widths match. + if (vector_width == other_vector_width) { result.push_back(inst); } } diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index f444695dc5..b91393ef87 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -1515,6 +1515,44 @@ const std::string kTestShader4 = R"( OpFunctionEnd )"; +// The SPIR-V comes from the following GLSL: +// +// #version 310 es +// precision highp float; +// +// layout(location = 0) out vec4 color; +// +// void main() +// { +// color = vec4(1.0, 0.0, 0.0, 1.0); +// } + +const std::string kTestShader5 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpDecorate %9 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 %11 %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %9 %12 + OpReturn + OpFunctionEnd + )"; + void AddConstantUniformFact(protobufs::FactSequence* facts, uint32_t descriptor_set, uint32_t binding, std::vector&& indices, uint32_t value) { @@ -1552,8 +1590,8 @@ void RunFuzzerAndReplayer(const std::string& shader, ASSERT_TRUE(t.Validate(binary_in)); std::vector donor_suppliers; - for (auto donor : - {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4}) { + for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4, + &kTestShader5}) { donor_suppliers.emplace_back([donor]() { return BuildModule(env, kConsoleMessageConsumer, *donor, kFuzzAssembleOption); @@ -1636,6 +1674,13 @@ TEST(FuzzerReplayerTest, Miscellaneous4) { RunFuzzerAndReplayer(kTestShader4, facts, 14, kNumFuzzerRuns); } +TEST(FuzzerReplayerTest, Miscellaneous5) { + // Do some fuzzer runs, starting from an initial seed of 29 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader5, protobufs::FactSequence(), 29, + kNumFuzzerRuns); +} + } // namespace } // namespace fuzz } // namespace spvtools From 6428ad05e706567945faf0fa0ae6257bb6006733 Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Thu, 12 Mar 2020 14:40:38 +0200 Subject: [PATCH 44/88] spirv-fuzz: Support OpPhi when adding dead break and continue (#3225) Fixes #2856. --- source/fuzz/fuzzer_pass_add_dead_breaks.cpp | 43 +++++++++++++++---- .../fuzz/fuzzer_pass_add_dead_continues.cpp | 43 ++++++++++++++++--- .../fuzz/transformation_add_dead_continue.cpp | 2 +- 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index fa6b098844..aefc2fcd75 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include "source/fuzz/fuzzer_pass_add_dead_breaks.h" - +#include "source/fuzz/fuzzer_util.h" #include "source/fuzz/transformation_add_dead_break.h" #include "source/opt/ir_context.h" @@ -34,11 +34,16 @@ void FuzzerPassAddDeadBreaks::Apply() { // We consider each function separately. for (auto& function : *GetIRContext()->module()) { // For a given function, we find all the merge blocks in that function. - std::vector merge_block_ids; + std::vector merge_blocks; for (auto& block : function) { auto maybe_merge_id = block.MergeBlockIdIfAny(); if (maybe_merge_id) { - merge_block_ids.push_back(maybe_merge_id); + auto merge_block = + fuzzerutil::MaybeFindBlock(GetIRContext(), maybe_merge_id); + + assert(merge_block && "Merge block can't be null"); + + merge_blocks.push_back(merge_block); } } // We rather aggressively consider the possibility of adding a break from @@ -46,12 +51,34 @@ void FuzzerPassAddDeadBreaks::Apply() { // inapplicable as they would be illegal. That's OK - we later discard the // ones that turn out to be no good. for (auto& block : function) { - for (auto merge_block_id : merge_block_ids) { - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right - // now we completely ignore OpPhi instructions at merge blocks. This - // will lead to interesting opportunities being missed. + for (auto* merge_block : merge_blocks) { + // Populate this vector with ids that are available at the branch point + // of this basic block. We will use these ids to update OpPhi + // instructions later. + std::vector phi_ids; + + // Determine how we need to adjust OpPhi instructions' operands + // for this transformation to be valid. + // + // If |block| has a branch to |merge_block|, the latter must have all of + // its OpPhi instructions set up correctly - we don't need to adjust + // anything. + if (!block.IsSuccessor(merge_block)) { + merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { + // Add an additional operand for OpPhi instruction. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): + // If we have a way to communicate to the fact manager + // that a specific id use is irrelevant and could be replaced with + // something else, we should add such a fact about the zero + // provided as an OpPhi operand + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id())); + }); + } + auto candidate_transformation = TransformationAddDeadBreak( - block.id(), merge_block_id, GetFuzzerContext()->ChooseEven(), {}); + block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(), + std::move(phi_ids)); if (candidate_transformation.IsApplicable(GetIRContext(), *GetFactManager())) { // Only consider a transformation as a candidate if it is applicable. diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp index 51bcb91ebe..852df3de6c 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include "source/fuzz/fuzzer_pass_add_dead_continues.h" - +#include "source/fuzz/fuzzer_util.h" #include "source/fuzz/transformation_add_dead_continue.h" #include "source/opt/ir_context.h" @@ -32,15 +32,46 @@ void FuzzerPassAddDeadContinues::Apply() { // Consider every block in every function. for (auto& function : *GetIRContext()->module()) { for (auto& block : function) { + // Get the label id of the continue target of the innermost loop. + auto continue_block_id = + block.IsLoopHeader() + ? block.ContinueBlockId() + : GetIRContext()->GetStructuredCFGAnalysis()->LoopContinueBlock( + block.id()); + + // This transformation is not applicable if current block is not inside a + // loop. + if (continue_block_id == 0) { + continue; + } + + auto* continue_block = + fuzzerutil::MaybeFindBlock(GetIRContext(), continue_block_id); + assert(continue_block && "Continue block is null"); + + // Analyze return type of each OpPhi instruction in the continue target + // and provide an id for the transformation if needed. + std::vector phi_ids; + // Check whether current block has an edge to the continue target. + // If this is the case, we don't need to do anything. + if (!block.IsSuccessor(continue_block)) { + continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { + // Add an additional operand for OpPhi instruction. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): + // If we have a way to communicate to the fact manager + // that a specific id use is irrelevant and could be replaced with + // something else, we should add such a fact about the zero + // provided as an OpPhi operand + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id())); + }); + } + // Make a transformation to add a dead continue from this node; if the // node turns out to be inappropriate (e.g. by not being in a loop) the // precondition for the transformation will fail and it will be ignored. - // - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right - // now we completely ignore OpPhi instructions at continue targets. - // This will lead to interesting opportunities being missed. auto candidate_transformation = TransformationAddDeadContinue( - block.id(), GetFuzzerContext()->ChooseEven(), {}); + block.id(), GetFuzzerContext()->ChooseEven(), std::move(phi_ids)); // Probabilistically decide whether to apply the transformation in the // case that it is applicable. if (candidate_transformation.IsApplicable(GetIRContext(), diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp index ffa182e930..3a4875e3dc 100644 --- a/source/fuzz/transformation_add_dead_continue.cpp +++ b/source/fuzz/transformation_add_dead_continue.cpp @@ -112,7 +112,7 @@ bool TransformationAddDeadContinue::IsApplicable( // clone, and check whether the transformed clone is valid. // // In principle some of the above checks could be removed, with more reliance - // being places on the validator. This should be revisited if we are sure + // being placed on the validator. This should be revisited if we are sure // the validator is complete with respect to checking structured control flow // rules. auto cloned_context = fuzzerutil::CloneIRContext(context); From 1fe9bcc10824c1fa35bd9b697188340132d39213 Mon Sep 17 00:00:00 2001 From: greg-lunarg Date: Thu, 12 Mar 2020 07:19:52 -0600 Subject: [PATCH 45/88] Instrument: Debug Printf support (#3215) Create a pass to instrument OpDebugPrintf instructions. This pass replaces all OpDebugPrintf instructions with instructions to write a record containing the string id and the all specified values into a special printf output buffer (if space allows). This pass is designed to support the printf validation in the Vulkan validation layers. Fixes #3210 --- Android.mk | 1 + BUILD.gn | 2 + include/spirv-tools/instrument.hpp | 3 + include/spirv-tools/optimizer.hpp | 12 ++ source/enum_set.h | 15 ++ source/opt/CMakeLists.txt | 2 + source/opt/feature_manager.cpp | 10 + source/opt/feature_manager.h | 6 + source/opt/inst_debug_printf_pass.cpp | 266 ++++++++++++++++++++++++++ source/opt/inst_debug_printf_pass.h | 96 ++++++++++ source/opt/instrument_pass.cpp | 53 +++-- source/opt/instrument_pass.h | 22 ++- source/opt/optimizer.cpp | 8 + source/opt/passes.h | 1 + test/opt/CMakeLists.txt | 1 + test/opt/inst_debug_printf_test.cpp | 215 +++++++++++++++++++++ 16 files changed, 693 insertions(+), 20 deletions(-) create mode 100644 source/opt/inst_debug_printf_pass.cpp create mode 100644 source/opt/inst_debug_printf_pass.h create mode 100644 test/opt/inst_debug_printf_test.cpp diff --git a/Android.mk b/Android.mk index db4f43bdf4..eec709af6b 100644 --- a/Android.mk +++ b/Android.mk @@ -119,6 +119,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/inline_opaque_pass.cpp \ source/opt/inst_bindless_check_pass.cpp \ source/opt/inst_buff_addr_check_pass.cpp \ + source/opt/inst_debug_printf_pass.cpp \ source/opt/instruction.cpp \ source/opt/instruction_list.cpp \ source/opt/instrument_pass.cpp \ diff --git a/BUILD.gn b/BUILD.gn index 1337059dee..d3107fd773 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -590,6 +590,8 @@ static_library("spvtools_opt") { "source/opt/inst_bindless_check_pass.h", "source/opt/inst_buff_addr_check_pass.cpp", "source/opt/inst_buff_addr_check_pass.h", + "source/opt/inst_debug_printf_pass.cpp", + "source/opt/inst_debug_printf_pass.h", "source/opt/instruction.cpp", "source/opt/instruction.h", "source/opt/instruction_list.cpp", diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp index 2dcb333160..d3180e4442 100644 --- a/include/spirv-tools/instrument.hpp +++ b/include/spirv-tools/instrument.hpp @@ -208,6 +208,9 @@ static const int kDebugInputBindingBindless = 1; // The binding for the input buffer read by InstBuffAddrCheckPass. static const int kDebugInputBindingBuffAddr = 2; +// This is the output buffer written by InstDebugPrintfPass. +static const int kDebugOutputPrintfStream = 3; + // Bindless Validation Input Buffer Format // // An input buffer for bindless validation consists of a single array of diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index c31ccef8c3..b904923230 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -791,6 +791,18 @@ Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version = 2); +// Create a pass to instrument OpDebugPrintf instructions. +// This pass replaces all OpDebugPrintf instructions with instructions to write +// a record containing the string id and the all specified values into a special +// printf output buffer (if space allows). This pass is designed to support +// the printf validation in the Vulkan validation layers. +// +// The instrumentation will write buffers in debug descriptor set |desc_set|. +// It will write |shader_id| in each output record to identify the shader +// module which generated the record. +Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set, + uint32_t shader_id); + // Create a pass to upgrade to the VulkanKHR memory model. // This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR. // Additionally, it modifies memory, image, atomic and barrier operations to diff --git a/source/enum_set.h b/source/enum_set.h index 2e7046d4ec..d4d31e3324 100644 --- a/source/enum_set.h +++ b/source/enum_set.h @@ -93,6 +93,10 @@ class EnumSet { // enum value is already in the set. void Add(EnumType c) { AddWord(ToWord(c)); } + // Removes the given enum value from the set. This has no effect if the + // enum value is not in the set. + void Remove(EnumType c) { RemoveWord(ToWord(c)); } + // Returns true if this enum value is in the set. bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); } @@ -141,6 +145,17 @@ class EnumSet { } } + // Removes the given enum value (as a 32-bit word) from the set. This has no + // effect if the enum value is not in the set. + void RemoveWord(uint32_t word) { + if (auto new_bits = AsMask(word)) { + mask_ &= ~new_bits; + } else { + auto itr = Overflow().find(word); + if (itr != Overflow().end()) Overflow().erase(itr); + } + } + // Returns true if the enum represented as a 32-bit word is in the set. bool ContainsWord(uint32_t word) const { // We shouldn't call Overflow() since this is a const method. diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 0f719cb908..1428c7465f 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -58,6 +58,7 @@ set(SPIRV_TOOLS_OPT_SOURCES inline_pass.h inst_bindless_check_pass.h inst_buff_addr_check_pass.h + inst_debug_printf_pass.h instruction.h instruction_list.h instrument_pass.h @@ -164,6 +165,7 @@ set(SPIRV_TOOLS_OPT_SOURCES inline_pass.cpp inst_bindless_check_pass.cpp inst_buff_addr_check_pass.cpp + inst_debug_printf_pass.cpp instruction.cpp instruction_list.cpp instrument_pass.cpp diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index 63d50b6d74..b4d6f1ba52 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -47,6 +47,11 @@ void FeatureManager::AddExtension(Instruction* ext) { } } +void FeatureManager::RemoveExtension(Extension ext) { + if (!extensions_.Contains(ext)) return; + extensions_.Remove(ext); +} + void FeatureManager::AddCapability(SpvCapability cap) { if (capabilities_.Contains(cap)) return; @@ -60,6 +65,11 @@ void FeatureManager::AddCapability(SpvCapability cap) { } } +void FeatureManager::RemoveCapability(SpvCapability cap) { + if (!capabilities_.Contains(cap)) return; + capabilities_.Remove(cap); +} + void FeatureManager::AddCapabilities(Module* module) { for (Instruction& inst : module->capabilities()) { AddCapability(static_cast(inst.GetSingleWordInOperand(0))); diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index 2fe3291088..881d5e601a 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h @@ -30,11 +30,17 @@ class FeatureManager { // Returns true if |ext| is an enabled extension in the module. bool HasExtension(Extension ext) const { return extensions_.Contains(ext); } + // Removes the given |extension| from the current FeatureManager. + void RemoveExtension(Extension extension); + // Returns true if |cap| is an enabled capability in the module. bool HasCapability(SpvCapability cap) const { return capabilities_.Contains(cap); } + // Removes the given |capability| from the current FeatureManager. + void RemoveCapability(SpvCapability capability); + // Analyzes |module| and records enabled extensions and capabilities. void Analyze(Module* module); diff --git a/source/opt/inst_debug_printf_pass.cpp b/source/opt/inst_debug_printf_pass.cpp new file mode 100644 index 0000000000..c0e6bc3f04 --- /dev/null +++ b/source/opt/inst_debug_printf_pass.cpp @@ -0,0 +1,266 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// Copyright (c) 2020 Valve Corporation +// Copyright (c) 2020 LunarG 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 "inst_debug_printf_pass.h" + +#include "spirv/unified1/NonSemanticDebugPrintf.h" + +namespace spvtools { +namespace opt { + +void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst, + std::vector* val_ids, + InstructionBuilder* builder) { + uint32_t val_ty_id = val_inst->type_id(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* val_ty = type_mgr->GetType(val_ty_id); + switch (val_ty->kind()) { + case analysis::Type::kVector: { + analysis::Vector* v_ty = val_ty->AsVector(); + const analysis::Type* c_ty = v_ty->element_type(); + uint32_t c_ty_id = type_mgr->GetId(c_ty); + for (uint32_t c = 0; c < v_ty->element_count(); ++c) { + Instruction* c_inst = builder->AddIdLiteralOp( + c_ty_id, SpvOpCompositeExtract, val_inst->result_id(), c); + GenOutputValues(c_inst, val_ids, builder); + } + return; + } + case analysis::Type::kBool: { + // Select between uint32 zero or one + uint32_t zero_id = builder->GetUintConstantId(0); + uint32_t one_id = builder->GetUintConstantId(1); + Instruction* sel_inst = builder->AddTernaryOp( + GetUintId(), SpvOpSelect, val_inst->result_id(), one_id, zero_id); + val_ids->push_back(sel_inst->result_id()); + return; + } + case analysis::Type::kFloat: { + analysis::Float* f_ty = val_ty->AsFloat(); + switch (f_ty->width()) { + case 16: { + // Convert float16 to float32 and recurse + Instruction* f32_inst = builder->AddUnaryOp( + GetFloatId(), SpvOpFConvert, val_inst->result_id()); + GenOutputValues(f32_inst, val_ids, builder); + return; + } + case 64: { + // Bitcast float64 to uint64 and recurse + Instruction* ui64_inst = builder->AddUnaryOp( + GetUint64Id(), SpvOpBitcast, val_inst->result_id()); + GenOutputValues(ui64_inst, val_ids, builder); + return; + } + case 32: { + // Bitcase float32 to uint32 + Instruction* bc_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast, + val_inst->result_id()); + val_ids->push_back(bc_inst->result_id()); + return; + } + default: + assert(false && "unsupported float width"); + return; + } + } + case analysis::Type::kInteger: { + analysis::Integer* i_ty = val_ty->AsInteger(); + switch (i_ty->width()) { + case 64: { + Instruction* ui64_inst = val_inst; + if (i_ty->IsSigned()) { + // Bitcast sint64 to uint64 + ui64_inst = builder->AddUnaryOp(GetUint64Id(), SpvOpBitcast, + val_inst->result_id()); + } + // Break uint64 into 2x uint32 + Instruction* lo_ui64_inst = builder->AddUnaryOp( + GetUintId(), SpvOpUConvert, ui64_inst->result_id()); + Instruction* rshift_ui64_inst = builder->AddBinaryOp( + GetUint64Id(), SpvOpShiftRightLogical, ui64_inst->result_id(), + builder->GetUintConstantId(32)); + Instruction* hi_ui64_inst = builder->AddUnaryOp( + GetUintId(), SpvOpUConvert, rshift_ui64_inst->result_id()); + val_ids->push_back(lo_ui64_inst->result_id()); + val_ids->push_back(hi_ui64_inst->result_id()); + return; + } + case 8: { + Instruction* ui8_inst = val_inst; + if (i_ty->IsSigned()) { + // Bitcast sint8 to uint8 + ui8_inst = builder->AddUnaryOp(GetUint8Id(), SpvOpBitcast, + val_inst->result_id()); + } + // Convert uint8 to uint32 + Instruction* ui32_inst = builder->AddUnaryOp( + GetUintId(), SpvOpUConvert, ui8_inst->result_id()); + val_ids->push_back(ui32_inst->result_id()); + return; + } + case 32: { + Instruction* ui32_inst = val_inst; + if (i_ty->IsSigned()) { + // Bitcast sint32 to uint32 + ui32_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast, + val_inst->result_id()); + } + // uint32 needs no further processing + val_ids->push_back(ui32_inst->result_id()); + return; + } + default: + // TODO(greg-lunarg): Support non-32-bit int + assert(false && "unsupported int width"); + return; + } + } + default: + assert(false && "unsupported type"); + return; + } +} + +void InstDebugPrintfPass::GenOutputCode( + Instruction* printf_inst, uint32_t stage_idx, + std::vector>* new_blocks) { + BasicBlock* back_blk_ptr = &*new_blocks->back(); + InstructionBuilder builder( + context(), back_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen debug printf record validation-specific values. The format string + // will have its id written. Vectors will need to be broken down into + // component values. float16 will need to be converted to float32. Pointer + // and uint64 will need to be converted to two uint32 values. float32 will + // need to be bitcast to uint32. int32 will need to be bitcast to uint32. + std::vector val_ids; + bool is_first_operand = false; + printf_inst->ForEachInId( + [&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) { + // skip set operand + if (!is_first_operand) { + is_first_operand = true; + return; + } + Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid); + if (opnd_inst->opcode() == SpvOpString) { + uint32_t string_id_id = builder.GetUintConstantId(*iid); + val_ids.push_back(string_id_id); + } else { + GenOutputValues(opnd_inst, &val_ids, &builder); + } + }); + GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids, + &builder); + context()->KillInst(printf_inst); +} + +void InstDebugPrintfPass::GenDebugPrintfCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + // If not DebugPrintf OpExtInst, return. + Instruction* printf_inst = &*ref_inst_itr; + if (printf_inst->opcode() != SpvOpExtInst) return; + if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return; + if (printf_inst->GetSingleWordInOperand(1) != + NonSemanticDebugPrintfDebugPrintf) + return; + // Initialize DefUse manager before dismantling module + (void)get_def_use_mgr(); + // Move original block's preceding instructions into first new block + std::unique_ptr new_blk_ptr; + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); + // Generate instructions to output printf args to printf buffer + GenOutputCode(printf_inst, stage_idx, new_blocks); + // Caller expects at least two blocks with last block containing remaining + // code, so end block after instrumentation, create remainder block, and + // branch to it + uint32_t rem_blk_id = TakeNextId(); + std::unique_ptr rem_label(NewLabel(rem_blk_id)); + BasicBlock* back_blk_ptr = &*new_blocks->back(); + InstructionBuilder builder( + context(), back_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + (void)builder.AddBranch(rem_blk_id); + // Gen remainder block + new_blk_ptr.reset(new BasicBlock(std::move(rem_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Move original block's remaining code into remainder block and add + // to new blocks + MovePostludeCode(ref_block_itr, &*new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); +} + +void InstDebugPrintfPass::InitializeInstDebugPrintf() { + // Initialize base class + InitializeInstrument(); +} + +Pass::Status InstDebugPrintfPass::ProcessImpl() { + // Perform printf instrumentation on each entry point function in module + InstProcessFunction pfn = + [this](BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, stage_idx, + new_blocks); + }; + (void)InstProcessEntryPointCallTree(pfn); + // Remove DebugPrintf OpExtInstImport instruction + Instruction* ext_inst_import_inst = + get_def_use_mgr()->GetDef(ext_inst_printf_id_); + context()->KillInst(ext_inst_import_inst); + // If no remaining non-semantic instruction sets, remove non-semantic debug + // info extension from module and feature manager + bool non_sem_set_seen = false; + for (auto c_itr = context()->module()->ext_inst_import_begin(); + c_itr != context()->module()->ext_inst_import_end(); ++c_itr) { + const char* set_name = + reinterpret_cast(&c_itr->GetInOperand(0).words[0]); + const char* non_sem_str = "NonSemantic."; + if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) { + non_sem_set_seen = true; + break; + } + } + if (!non_sem_set_seen) { + for (auto c_itr = context()->module()->extension_begin(); + c_itr != context()->module()->extension_end(); ++c_itr) { + const char* ext_name = + reinterpret_cast(&c_itr->GetInOperand(0).words[0]); + if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + context()->KillInst(&*c_itr); + break; + } + } + context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info); + } + return Status::SuccessWithChange; +} + +Pass::Status InstDebugPrintfPass::Process() { + ext_inst_printf_id_ = + get_module()->GetExtInstImportId("NonSemantic.DebugPrintf"); + if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange; + InitializeInstDebugPrintf(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/inst_debug_printf_pass.h b/source/opt/inst_debug_printf_pass.h new file mode 100644 index 0000000000..2968a203af --- /dev/null +++ b/source/opt/inst_debug_printf_pass.h @@ -0,0 +1,96 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// Copyright (c) 2020 Valve Corporation +// Copyright (c) 2020 LunarG 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. + +#ifndef LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_ +#define LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_ + +#include "instrument_pass.h" + +namespace spvtools { +namespace opt { + +// This class/pass is designed to support the debug printf GPU-assisted layer +// of https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and +// external design may change as the layer evolves. +class InstDebugPrintfPass : public InstrumentPass { + public: + // For test harness only + InstDebugPrintfPass() + : InstrumentPass(7, 23, kInstValidationIdDebugPrintf, 2) {} + // For all other interfaces + InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id) + : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf, 2) {} + + ~InstDebugPrintfPass() override = default; + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "inst-printf-pass"; } + + private: + // Generate instructions for OpDebugPrintf. + // + // If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result + // of replacing it with buffer write instructions within its block at + // |ref_block_itr|. The instructions write a record to the printf + // output buffer stream including |function_idx, instruction_idx, stage_idx| + // and removes the OpDebugPrintf. The block at |ref_block_itr| can just be + // replaced with the block in |new_blocks|. Besides the buffer writes, this + // block will comprise all instructions preceding and following + // |ref_inst_itr|. + // + // This function is designed to be passed to + // InstrumentPass::InstProcessEntryPointCallTree(), which applies the + // function to each instruction in a module and replaces the instruction + // if warranted. + // + // This instrumentation function utilizes GenDebugStreamWrite() to write its + // error records. The validation-specific part of the error record will + // consist of a uint32 which is the id of the format string plus a sequence + // of uint32s representing the values of the remaining operands of the + // DebugPrintf. + void GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, + uint32_t stage_idx, + std::vector>* new_blocks); + + // Generate a sequence of uint32 instructions in |builder| (if necessary) + // representing the value of |val_inst|, which must be a buffer pointer, a + // uint64, or a scalar or vector of type uint32, float32 or float16. Append + // the ids of all values to the end of |val_ids|. + void GenOutputValues(Instruction* val_inst, std::vector* val_ids, + InstructionBuilder* builder); + + // Generate instructions to write a record containing the operands of + // |printf_inst| arguments to printf buffer, adding new code to the end of + // the last block in |new_blocks|. Kill OpDebugPrintf instruction. + void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx, + std::vector>* new_blocks); + + // Initialize state for instrumenting bindless checking + void InitializeInstDebugPrintf(); + + // Apply GenDebugPrintfCode to every instruction in module. + Pass::Status ProcessImpl(); + + uint32_t ext_inst_printf_id_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_ diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp index b1a6edb9de..c8c6c21130 100644 --- a/source/opt/instrument_pass.cpp +++ b/source/opt/instrument_pass.cpp @@ -380,6 +380,8 @@ uint32_t InstrumentPass::GetOutputBufferBinding() { return kDebugOutputBindingStream; case kInstValidationIdBuffAddr: return kDebugOutputBindingStream; + case kInstValidationIdDebugPrintf: + return kDebugOutputPrintfStream; default: assert(false && "unexpected validation id"); } @@ -529,6 +531,16 @@ uint32_t InstrumentPass::GetInputBufferId() { return input_buffer_id_; } +uint32_t InstrumentPass::GetFloatId() { + if (float_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + float_id_ = type_mgr->GetTypeInstruction(reg_float_ty); + } + return float_id_; +} + uint32_t InstrumentPass::GetVec4FloatId() { if (v4float_id_ == 0) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -561,6 +573,16 @@ uint32_t InstrumentPass::GetUint64Id() { return uint64_id_; } +uint32_t InstrumentPass::GetUint8Id() { + if (uint8_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint8_ty(8, false); + analysis::Type* reg_uint8_ty = type_mgr->GetRegisteredType(&uint8_ty); + uint8_id_ = type_mgr->GetTypeInstruction(reg_uint8_ty); + } + return uint8_id_; +} + uint32_t InstrumentPass::GetVecUintId(uint32_t len) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); analysis::Integer uint_ty(32, false); @@ -606,21 +628,22 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, // Total param count is common params plus validation-specific // params uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt; - if (output_func_id_ == 0) { + if (param2output_func_id_[param_cnt] == 0) { // Create function - output_func_id_ = TakeNextId(); + param2output_func_id_[param_cnt] = TakeNextId(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); std::vector param_types; for (uint32_t c = 0; c < param_cnt; ++c) param_types.push_back(type_mgr->GetType(GetUintId())); analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types); analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); - std::unique_ptr func_inst(new Instruction( - get_module()->context(), SpvOpFunction, GetVoidId(), output_func_id_, - {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, - {SpvFunctionControlMaskNone}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {type_mgr->GetTypeInstruction(reg_func_ty)}}})); + std::unique_ptr func_inst( + new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(), + param2output_func_id_[param_cnt], + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvFunctionControlMaskNone}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {type_mgr->GetTypeInstruction(reg_func_ty)}}})); get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); std::unique_ptr output_func = MakeUnique(std::move(func_inst)); @@ -709,10 +732,8 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); output_func->SetFunctionEnd(std::move(func_end_inst)); context()->AddFunction(std::move(output_func)); - output_func_param_cnt_ = param_cnt; } - assert(param_cnt == output_func_param_cnt_ && "bad arg count"); - return output_func_id_; + return param2output_func_id_[param_cnt]; } uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { @@ -848,7 +869,7 @@ bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn, std::unordered_set done; // Don't process input and output functions for (auto& ifn : param2input_func_id_) done.insert(ifn.second); - if (output_func_id_ != 0) done.insert(output_func_id_); + for (auto& ofn : param2output_func_id_) done.insert(ofn.second); // Process all functions from roots while (!roots->empty()) { const uint32_t fi = roots->front(); @@ -926,12 +947,12 @@ void InstrumentPass::InitializeInstrument() { output_buffer_id_ = 0; output_buffer_ptr_id_ = 0; input_buffer_ptr_id_ = 0; - output_func_id_ = 0; - output_func_param_cnt_ = 0; input_buffer_id_ = 0; + float_id_ = 0; v4float_id_ = 0; uint_id_ = 0; uint64_id_ = 0; + uint8_id_ = 0; v4uint_id_ = 0; v3uint_id_ = 0; bool_id_ = 0; @@ -944,6 +965,10 @@ void InstrumentPass::InitializeInstrument() { id2function_.clear(); id2block_.clear(); + // clear maps + param2input_func_id_.clear(); + param2output_func_id_.clear(); + // Initialize function and block maps. for (auto& fn : *get_module()) { id2function_[fn.result_id()] = &fn; diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h index 02568fb7ad..11afdce82b 100644 --- a/source/opt/instrument_pass.h +++ b/source/opt/instrument_pass.h @@ -61,6 +61,7 @@ namespace opt { // its output buffers. static const uint32_t kInstValidationIdBindless = 0; static const uint32_t kInstValidationIdBuffAddr = 1; +static const uint32_t kInstValidationIdDebugPrintf = 2; class InstrumentPass : public Pass { using cbb_ptr = const BasicBlock*; @@ -227,9 +228,12 @@ class InstrumentPass : public Pass { // Return id for 32-bit unsigned type uint32_t GetUintId(); - // Return id for 32-bit unsigned type + // Return id for 64-bit unsigned type uint32_t GetUint64Id(); + // Return id for 8-bit unsigned type + uint32_t GetUint8Id(); + // Return id for 32-bit unsigned type uint32_t GetBoolId(); @@ -267,6 +271,9 @@ class InstrumentPass : public Pass { // Return id for debug input buffer uint32_t GetInputBufferId(); + // Return id for 32-bit float type + uint32_t GetFloatId(); + // Return id for v4float type uint32_t GetVec4FloatId(); @@ -383,17 +390,17 @@ class InstrumentPass : public Pass { uint32_t input_buffer_ptr_id_; // id for debug output function - uint32_t output_func_id_; + std::unordered_map param2output_func_id_; // ids for debug input functions std::unordered_map param2input_func_id_; - // param count for output function - uint32_t output_func_param_cnt_; - // id for input buffer variable uint32_t input_buffer_id_; + // id for 32-bit float type + uint32_t float_id_; + // id for v4float type uint32_t v4float_id_; @@ -406,9 +413,12 @@ class InstrumentPass : public Pass { // id for 32-bit unsigned type uint32_t uint_id_; - // id for 32-bit unsigned type + // id for 64-bit unsigned type uint32_t uint64_id_; + // id for 8-bit unsigned type + uint32_t uint8_id_; + // id for bool type uint32_t bool_id_; diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 241aa75bef..6e271f5384 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -425,6 +425,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateConvertRelaxedToHalfPass()); } else if (pass_name == "relax-float-ops") { RegisterPass(CreateRelaxFloatOpsPass()); + } else if (pass_name == "inst-debug-printf") { + RegisterPass(CreateInstDebugPrintfPass(7, 23)); } else if (pass_name == "simplify-instructions") { RegisterPass(CreateSimplificationPass()); } else if (pass_name == "ssa-rewrite") { @@ -886,6 +888,12 @@ Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, input_init_enable, version)); } +Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set, + uint32_t shader_id) { + return MakeUnique( + MakeUnique(desc_set, shader_id)); +} + Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version) { diff --git a/source/opt/passes.h b/source/opt/passes.h index 1a3675c776..5b4ab898e5 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -46,6 +46,7 @@ #include "source/opt/inline_opaque_pass.h" #include "source/opt/inst_bindless_check_pass.h" #include "source/opt/inst_buff_addr_check_pass.h" +#include "source/opt/inst_debug_printf_pass.h" #include "source/opt/legalize_vector_shuffle_pass.h" #include "source/opt/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 327f265632..395433896a 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -55,6 +55,7 @@ add_spvtools_unittest(TARGET opt insert_extract_elim_test.cpp inst_bindless_check_test.cpp inst_buff_addr_check_test.cpp + inst_debug_printf_test.cpp instruction_list_test.cpp instruction_test.cpp ir_builder.cpp diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp new file mode 100644 index 0000000000..8123ffbbec --- /dev/null +++ b/test/opt/inst_debug_printf_test.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2020 Valve Corporation +// Copyright (c) 2020 LunarG 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. + +// Debug Printf Instrumentation Tests. + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InstDebugPrintfTest = PassTest<::testing::Test>; + +TEST_F(InstDebugPrintfTest, V4Float32) { + // SamplerState g_sDefault; + // Texture2D g_tColor; + // + // struct PS_INPUT + // { + // float2 vBaseTexCoord : TEXCOORD0; + // }; + // + // struct PS_OUTPUT + // { + // float4 vDiffuse : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT o; + // + // o.vDiffuse.rgba = g_tColor.Sample(g_sDefault, (i.vBaseTexCoord.xy).xy); + // debugPrintfEXT("diffuse: %v4f", o.vDiffuse.rgba); + // return o; + // } + + const std::string defs = + R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.DebugPrintf" +; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info" +; CHECK-NOT: %1 = OpExtInstImport "NonSemantic.DebugPrintf" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "MainPs" %3 %4 +; CHECK: OpEntryPoint Fragment %2 "MainPs" %3 %4 %gl_FragCoord +OpExecutionMode %2 OriginUpperLeft +%5 = OpString "Color is %vn" +)"; + + const std::string decorates = + R"(OpDecorate %6 DescriptorSet 0 +OpDecorate %6 Binding 1 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +OpDecorate %3 Location 0 +OpDecorate %4 Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +; CHECK: OpDecorate %_struct_47 Block +; CHECK: OpMemberDecorate %_struct_47 0 Offset 0 +; CHECK: OpMemberDecorate %_struct_47 1 Offset 4 +; CHECK: OpDecorate %49 DescriptorSet 7 +; CHECK: OpDecorate %49 Binding 3 +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)"; + + const std::string globals = + R"(%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%13 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%6 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%15 = OpTypeSampler +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 +%7 = OpVariable %_ptr_UniformConstant_15 UniformConstant +%17 = OpTypeSampledImage %13 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%3 = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%4 = OpVariable %_ptr_Output_v4float Output +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +; CHECK: %_struct_47 = OpTypeStruct %uint %_runtimearr_uint +; CHECK: %_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47 +; CHECK: %49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +)"; + + const std::string main = + R"(%2 = OpFunction %void None %9 +%20 = OpLabel +%21 = OpLoad %v2float %3 +%22 = OpLoad %13 %6 +%23 = OpLoad %15 %7 +%24 = OpSampledImage %17 %22 %23 +%25 = OpImageSampleImplicitLod %v4float %24 %21 +%26 = OpExtInst %void %1 1 %5 %25 +; CHECK-NOT: %26 = OpExtInst %void %1 1 %5 %25 +; CHECK: %29 = OpCompositeExtract %float %25 0 +; CHECK: %30 = OpBitcast %uint %29 +; CHECK: %31 = OpCompositeExtract %float %25 1 +; CHECK: %32 = OpBitcast %uint %31 +; CHECK: %33 = OpCompositeExtract %float %25 2 +; CHECK: %34 = OpBitcast %uint %33 +; CHECK: %35 = OpCompositeExtract %float %25 3 +; CHECK: %36 = OpBitcast %uint %35 +; CHECK: %101 = OpFunctionCall %void %37 %uint_36 %uint_5 %30 %32 %34 %36 +; CHECK: OpBranch %102 +; CHECK: %102 = OpLabel +OpStore %4 %25 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(; CHECK: %37 = OpFunction %void None %38 +; CHECK: %39 = OpFunctionParameter %uint +; CHECK: %40 = OpFunctionParameter %uint +; CHECK: %41 = OpFunctionParameter %uint +; CHECK: %42 = OpFunctionParameter %uint +; CHECK: %43 = OpFunctionParameter %uint +; CHECK: %44 = OpFunctionParameter %uint +; CHECK: %45 = OpLabel +; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0 +; CHECK: %55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_12 +; CHECK: %56 = OpIAdd %uint %55 %uint_12 +; CHECK: %57 = OpArrayLength %uint %49 1 +; CHECK: %59 = OpULessThanEqual %bool %56 %57 +; CHECK: OpSelectionMerge %60 None +; CHECK: OpBranchConditional %59 %61 %60 +; CHECK: %61 = OpLabel +; CHECK: %62 = OpIAdd %uint %55 %uint_0 +; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %62 +; CHECK: OpStore %64 %uint_12 +; CHECK: %66 = OpIAdd %uint %55 %uint_1 +; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66 +; CHECK: OpStore %67 %uint_23 +; CHECK: %69 = OpIAdd %uint %55 %uint_2 +; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69 +; CHECK: OpStore %70 %39 +; CHECK: %72 = OpIAdd %uint %55 %uint_3 +; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %72 +; CHECK: OpStore %73 %uint_4 +; CHECK: %76 = OpLoad %v4float %gl_FragCoord +; CHECK: %78 = OpBitcast %v4uint %76 +; CHECK: %79 = OpCompositeExtract %uint %78 0 +; CHECK: %80 = OpIAdd %uint %55 %uint_4 +; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %80 +; CHECK: OpStore %81 %79 +; CHECK: %82 = OpCompositeExtract %uint %78 1 +; CHECK: %83 = OpIAdd %uint %55 %uint_5 +; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %83 +; CHECK: OpStore %84 %82 +; CHECK: %86 = OpIAdd %uint %55 %uint_7 +; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %86 +; CHECK: OpStore %87 %40 +; CHECK: %89 = OpIAdd %uint %55 %uint_8 +; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %89 +; CHECK: OpStore %90 %41 +; CHECK: %92 = OpIAdd %uint %55 %uint_9 +; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %92 +; CHECK: OpStore %93 %42 +; CHECK: %95 = OpIAdd %uint %55 %uint_10 +; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %95 +; CHECK: OpStore %96 %43 +; CHECK: %98 = OpIAdd %uint %55 %uint_11 +; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %98 +; CHECK: OpStore %99 %44 +; CHECK: OpBranch %60 +; CHECK: %60 = OpLabel +; CHECK: OpReturn +; CHECK: OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch( + defs + decorates + globals + main + output_func, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Compute shader +// Geometry shader +// Tesselation control shader +// Tesselation eval shader +// Vertex shader + +} // namespace +} // namespace opt +} // namespace spvtools From 7a8f79762c4aafa8679cc653f21995ffa3a5dcd2 Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Fri, 13 Mar 2020 22:23:01 +0200 Subject: [PATCH 46/88] Update dependencies (#3228) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 23c41119b5..b4d0e1a9b3 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d', 'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528', 're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb', - 'spirv_headers_revision': 'dc77030acc9c6fe7ca21fff54c5a9d7b532d7da6', + 'spirv_headers_revision': '30ef660ce2e666f7ae925598b8a267f4da6d33aa', } deps = { From 25ede1ced6797f9a3689ae7dac5085ce8236c573 Mon Sep 17 00:00:00 2001 From: Jamie Madill Date: Mon, 16 Mar 2020 11:42:05 -0400 Subject: [PATCH 47/88] Roll external/spirv-headers/ 30ef660ce..a17e17e36 (1 commit) (#3230) https://github.com/KhronosGroup/SPIRV-Headers/compare/30ef660ce2e6...a17e17e36da4 $ git log 30ef660ce..a17e17e36 --date=short --no-merges --format='%ad %ae %s' 2020-03-13 jmadill Add missing header to BUILD.gn. Created with: roll-dep external/spirv-headers --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index b4d0e1a9b3..e13ca0da90 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d', 'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528', 're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb', - 'spirv_headers_revision': '30ef660ce2e666f7ae925598b8a267f4da6d33aa', + 'spirv_headers_revision': 'a17e17e36da44d2cd1740132ecd7a8cb078f1d15', } deps = { From 5a97e3a391677ff9baae8ccdf5c03c4d403d848b Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Tue, 17 Mar 2020 15:30:19 -0400 Subject: [PATCH 48/88] Add support for KHR_ray_{query,tracing} extensions (#3235) Update validator for SPV_KHR_ray_tracing. * Added handling for new enum types * Add SpvScopeShaderCallKHR as a valid scope * update spirv-headers Co-authored-by: alelenv Co-authored-by: Torosdagli Co-authored-by: Tobias Hector Co-authored-by: Steven Perron --- DEPS | 2 +- include/spirv-tools/libspirv.h | 10 +++++++++- source/binary.cpp | 8 +++++++- source/disassemble.cpp | 8 +++++++- source/opcode.cpp | 7 ++++++- source/operand.cpp | 16 +++++++++++++++- source/opt/ir_context.cpp | 2 ++ source/opt/local_access_chain_convert_pass.cpp | 1 + source/opt/local_single_block_elim_pass.cpp | 1 + source/opt/local_single_store_elim_pass.cpp | 1 + source/opt/reflect.h | 2 ++ source/val/validate_builtins.cpp | 10 +++++++--- source/val/validate_memory.cpp | 8 +++++++- source/val/validate_scopes.cpp | 1 + test/operand_capabilities_test.cpp | 11 +++++++++-- test/opt/ir_builder.cpp | 2 +- test/opt/type_manager_test.cpp | 2 +- test/val/val_memory_test.cpp | 10 ++++++---- utils/check_copyright.py | 5 +++-- 19 files changed, 87 insertions(+), 20 deletions(-) diff --git a/DEPS b/DEPS index e13ca0da90..c3e78a2647 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d', 'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528', 're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb', - 'spirv_headers_revision': 'a17e17e36da44d2cd1740132ecd7a8cb078f1d15', + 'spirv_headers_revision': 'f8bf11a0253a32375c32cad92c841237b96696c0', } deps = { diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 21a9608f8c..03c7d1bf30 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -1,4 +1,6 @@ -// Copyright (c) 2015-2016 The Khronos Group Inc. +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -165,6 +167,12 @@ typedef enum spv_operand_type_t { SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS, // SPIR-V Sec 3.29 SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO, // SPIR-V Sec 3.30 SPV_OPERAND_TYPE_CAPABILITY, // SPIR-V Sec 3.31 + SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF + SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection + SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCommitted + SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCandidate // Set 5: Operands that are a single word bitmask. // Sometimes a set bit indicates the instruction requires still more operands. diff --git a/source/binary.cpp b/source/binary.cpp index 0463061850..f16bf52275 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -1,4 +1,6 @@ -// Copyright (c) 2015-2016 The Khronos Group Inc. +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -633,6 +635,10 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_GROUP_OPERATION: case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: diff --git a/source/disassemble.cpp b/source/disassemble.cpp index 4b3972b51d..af30ce0be2 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -1,4 +1,6 @@ -// Copyright (c) 2015-2016 The Khronos Group Inc. +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -277,6 +279,10 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, case SPV_OPERAND_TYPE_GROUP_OPERATION: case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: diff --git a/source/opcode.cpp b/source/opcode.cpp index b38b5d4a02..80fe3b3a9f 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -1,4 +1,6 @@ -// Copyright (c) 2015-2016 The Khronos Group Inc. +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -333,6 +335,9 @@ int32_t spvOpcodeGeneratesType(SpvOp op) { case SpvOpTypeNamedBarrier: case SpvOpTypeAccelerationStructureNV: case SpvOpTypeCooperativeMatrixNV: + // case SpvOpTypeAccelerationStructureKHR: covered by + // SpvOpTypeAccelerationStructureNV + case SpvOpTypeRayQueryProvisionalKHR: return true; default: // In particular, OpTypeForwardPointer does not generate a type, diff --git a/source/operand.cpp b/source/operand.cpp index 304260668d..755ad6ac78 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -1,4 +1,6 @@ -// Copyright (c) 2015-2016 The Khronos Group Inc. +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -216,6 +218,14 @@ const char* spvOperandTypeStr(spv_operand_type_t type) { return "kernel profiling info"; case SPV_OPERAND_TYPE_CAPABILITY: return "capability"; + case SPV_OPERAND_TYPE_RAY_FLAGS: + return "ray flags"; + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + return "ray query intersection"; + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + return "ray query committed intersection type"; + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + return "ray query candidate intersection type"; case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: return "image"; @@ -323,6 +333,10 @@ bool spvOperandIsConcrete(spv_operand_type_t type) { case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 7bca29b201..72993fd647 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -385,6 +385,8 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) { SpvOpTypeSampler, SpvOpTypeSampledImage, SpvOpTypeAccelerationStructureNV, + SpvOpTypeAccelerationStructureKHR, + SpvOpTypeRayQueryProvisionalKHR, SpvOpTypeArray, SpvOpTypeRuntimeArray, SpvOpTypeStruct, diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index 19215967ae..e5cdc24538 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -378,6 +378,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", + "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", }); } diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index aebbd000fc..a465483c65 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -255,6 +255,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", + "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", }); diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index d6beeab296..4c71ce1ca8 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -118,6 +118,7 @@ void LocalSingleStoreElimPass::InitExtensionWhiteList() { "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", + "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", }); diff --git a/source/opt/reflect.h b/source/opt/reflect.h index 8106442881..51d23a7406 100644 --- a/source/opt/reflect.h +++ b/source/opt/reflect.h @@ -46,6 +46,8 @@ inline bool IsTypeInst(SpvOp opcode) { return (opcode >= SpvOpTypeVoid && opcode <= SpvOpTypeForwardPointer) || opcode == SpvOpTypePipeStorage || opcode == SpvOpTypeNamedBarrier || opcode == SpvOpTypeAccelerationStructureNV || + opcode == SpvOpTypeAccelerationStructureKHR || + opcode == SpvOpTypeRayQueryProvisionalKHR || opcode == SpvOpTypeCooperativeMatrixNV; } inline bool IsConstantInst(SpvOp opcode) { diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index 7623d49c29..d86c91e4ea 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp @@ -2263,8 +2263,11 @@ spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition( spv_result_t BuiltInsValidator::ValidateVertexIdOrInstanceIdAtDefinition( const Decoration& decoration, const Instruction& inst) { const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); - bool allow_instance_id = _.HasCapability(SpvCapabilityRayTracingNV) && - label == SpvBuiltInInstanceId; + bool allow_instance_id = + (_.HasCapability(SpvCapabilityRayTracingNV) || + _.HasCapability(SpvCapabilityRayTracingProvisionalKHR)) && + label == SpvBuiltInInstanceId; + if (spvIsVulkanEnv(_.context()->target_env) && !allow_instance_id) { return _.diag(SPV_ERROR_INVALID_DATA, &inst) << "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId " @@ -3085,7 +3088,8 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInWorldToObjectNV: case SpvBuiltInHitTNV: case SpvBuiltInHitKindNV: - case SpvBuiltInIncomingRayFlagsNV: { + case SpvBuiltInIncomingRayFlagsNV: + case SpvBuiltInRayGeometryIndexKHR: { // No validation rules (for the moment). break; } diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index bff8b20ee5..1e1a38dd22 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -1,4 +1,6 @@ // Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -533,7 +535,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (!IsAllowedTypeOrArrayOfSame( _, pointee, {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, - SpvOpTypeAccelerationStructureNV})) { + SpvOpTypeAccelerationStructureNV, + SpvOpTypeAccelerationStructureKHR, + SpvOpTypeRayQueryProvisionalKHR})) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "UniformConstant OpVariable '" << _.getIdName(inst->id()) << "' has illegal type.\n" @@ -542,6 +546,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { << "are used only as handles to refer to opaque resources. Such " << "variables must be typed as OpTypeImage, OpTypeSampler, " << "OpTypeSampledImage, OpTypeAccelerationStructureNV, " + "OpTypeAccelerationStructureKHR, " + "OpTypeRayQueryProvisionalKHR, " << "or an array of one of these types."; } } diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index 473d6e840c..ea3ebcb66e 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp @@ -32,6 +32,7 @@ bool IsValidScope(uint32_t scope) { case SpvScopeSubgroup: case SpvScopeInvocation: case SpvScopeQueueFamilyKHR: + case SpvScopeShaderCallKHR: return true; case SpvScopeMax: break; diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp index 11955970ea..addb08a793 100644 --- a/test/operand_capabilities_test.cpp +++ b/test/operand_capabilities_test.cpp @@ -82,6 +82,13 @@ TEST_P(EnumCapabilityTest, Sample) { SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3 \ } \ } +#define CASE4(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ + SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \ + SpvCapability##CAP4 \ + } \ + } #define CASE5(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5) \ { \ SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ @@ -491,8 +498,8 @@ INSTANTIATE_TEST_SUITE_P( CASE1(BUILT_IN, BuiltInCullDistance, CullDistance), // Bug 1407, 15234 CASE1(BUILT_IN, BuiltInVertexId, Shader), CASE1(BUILT_IN, BuiltInInstanceId, Shader), - CASE3(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation, - RayTracingNV), + CASE4(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation, + RayTracingNV, RayTracingProvisionalKHR), CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation), CASE2(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT), CASE2(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT), // Bug 15234 diff --git a/test/opt/ir_builder.cpp b/test/opt/ir_builder.cpp index f800ca437e..cb234e0045 100644 --- a/test/opt/ir_builder.cpp +++ b/test/opt/ir_builder.cpp @@ -408,7 +408,7 @@ OpFunctionEnd TEST_F(IRBuilderTest, AccelerationStructureNV) { const std::string text = R"( -; CHECK: OpTypeAccelerationStructureNV +; CHECK: OpTypeAccelerationStructureKHR OpCapability Shader OpCapability RayTracingNV OpExtension "SPV_NV_ray_tracing" diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp index 743d0b616a..fdae2efc40 100644 --- a/test/opt/type_manager_test.cpp +++ b/test/opt/type_manager_test.cpp @@ -1063,7 +1063,7 @@ TEST(TypeManager, GetTypeInstructionAllTypes) { ; CHECK: OpTypeForwardPointer [[uniform_ptr]] Uniform ; CHECK: OpTypePipeStorage ; CHECK: OpTypeNamedBarrier -; CHECK: OpTypeAccelerationStructureNV +; CHECK: OpTypeAccelerationStructureKHR ; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]] OpCapability Shader OpCapability Int64 diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 22761cc1e1..b32867b16c 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -57,8 +57,9 @@ OpFunctionEnd "Variables identified with the UniformConstant storage class " "are used only as handles to refer to opaque resources. Such " "variables must be typed as OpTypeImage, OpTypeSampler, " - "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an " - "array of one of these types.")); + "OpTypeSampledImage, OpTypeAccelerationStructureNV, " + "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, " + "or an array of one of these types.")); } TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceGood) { @@ -110,8 +111,9 @@ OpFunctionEnd "Variables identified with the UniformConstant storage class " "are used only as handles to refer to opaque resources. Such " "variables must be typed as OpTypeImage, OpTypeSampler, " - "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an " - "array of one of these types.")); + "OpTypeSampledImage, OpTypeAccelerationStructureNV, " + "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, " + "or an array of one of these types.")); } TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceArrayGood) { diff --git a/utils/check_copyright.py b/utils/check_copyright.py index b2283009de..4467a32519 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -34,10 +34,11 @@ 'Pierre Moreau', 'Samsung Inc', 'André Perez Maselco', - 'Vasyl Teliman'] + 'Vasyl Teliman', + 'Advanced Micro Devices, Inc.'] CURRENT_YEAR='2020' -YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)' +YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)' COPYRIGHT_RE = re.compile( 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS))) From 18d3896a15fc90e71b9eb58901b2eb1728185115 Mon Sep 17 00:00:00 2001 From: Ehsan Date: Tue, 17 Mar 2020 22:36:02 -0500 Subject: [PATCH 49/88] Whitelist SPV_EXT_demote_to_helper_invocation for opt passes (#3236) --- source/opt/aggressive_dead_code_elim_pass.cpp | 1 + source/opt/local_access_chain_convert_pass.cpp | 1 + source/opt/local_single_block_elim_pass.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 761ff7c336..2cdf5fff46 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -923,6 +923,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", "SPV_NV_compute_shader_derivatives", diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index e5cdc24538..a8cf94b041 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -371,6 +371,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", "SPV_NV_compute_shader_derivatives", diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index a465483c65..c66271ef04 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -248,6 +248,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_NV_fragment_shader_barycentric", "SPV_NV_compute_shader_derivatives", From da52d0875cccb999c615819a1a6b2d5be79ea2ee Mon Sep 17 00:00:00 2001 From: JiaoluAMD Date: Fri, 20 Mar 2020 00:41:30 +0800 Subject: [PATCH 50/88] Add RayQueryProvisionalKHR to opt types (#3239) Add missing RayQueryProvisionalKHR types --- source/opt/type_manager.cpp | 3 +++ source/opt/types.cpp | 3 +++ source/opt/types.h | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp index 166b8281f6..27c7199452 100644 --- a/source/opt/type_manager.cpp +++ b/source/opt/type_manager.cpp @@ -862,6 +862,9 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) { inst.GetSingleWordInOperand(2), inst.GetSingleWordInOperand(3)); break; + case SpvOpTypeRayQueryProvisionalKHR: + type = new RayQueryProvisionalKHR(); + break; default: SPIRV_UNIMPLEMENTED(consumer_, "unhandled type"); break; diff --git a/source/opt/types.cpp b/source/opt/types.cpp index 17f8fe920f..426d3ea445 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp @@ -128,6 +128,7 @@ std::unique_ptr Type::Clone() const { DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(RayQueryProvisionalKHR); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -173,6 +174,7 @@ bool Type::operator==(const Type& other) const { DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(RayQueryProvisionalKHR); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -223,6 +225,7 @@ void Type::GetHashWords(std::vector* words, DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(RayQueryProvisionalKHR); #undef DeclareKindCase default: assert(false && "Unhandled type"); diff --git a/source/opt/types.h b/source/opt/types.h index 69071ea177..ebeb476039 100644 --- a/source/opt/types.h +++ b/source/opt/types.h @@ -59,6 +59,7 @@ class PipeStorage; class NamedBarrier; class AccelerationStructureNV; class CooperativeMatrixNV; +class RayQueryProvisionalKHR; // Abstract class for a SPIR-V type. It has a bunch of As() methods, // which is used as a way to probe the actual . @@ -94,7 +95,8 @@ class Type { kPipeStorage, kNamedBarrier, kAccelerationStructureNV, - kCooperativeMatrixNV + kCooperativeMatrixNV, + kRayQueryProvisionalKHR }; Type(Kind k) : kind_(k) {} @@ -199,6 +201,7 @@ class Type { DeclareCastMethod(NamedBarrier) DeclareCastMethod(AccelerationStructureNV) DeclareCastMethod(CooperativeMatrixNV) + DeclareCastMethod(RayQueryProvisionalKHR) #undef DeclareCastMethod protected: @@ -659,6 +662,7 @@ DefineParameterlessType(Queue, queue); DefineParameterlessType(PipeStorage, pipe_storage); DefineParameterlessType(NamedBarrier, named_barrier); DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV); +DefineParameterlessType(RayQueryProvisionalKHR, rayQueryProvisionalKHR); #undef DefineParameterlessType } // namespace analysis From 60104cd97446877dad8ed1010a635218937a2f18 Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 19 Mar 2020 09:44:28 -0700 Subject: [PATCH 51/88] Add opt::Operand::AsCString and AsString (#3240) It only works when the operand is a literal string. --- source/opt/instruction.h | 9 +++++++++ test/opt/instruction_test.cpp | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 322e0aac0c..63dfa87732 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -80,6 +80,15 @@ struct Operand { spv_operand_type_t type; // Type of this logical operand. OperandData words; // Binary segments of this logical operand. + // Returns a string operand as a C-style string. + const char* AsCString() const { + assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); + return reinterpret_cast(words.data()); + } + + // Returns a string operand as a std::string. + std::string AsString() const { return AsCString(); } + friend bool operator==(const Operand& o1, const Operand& o2) { return o1.type == o2.type && o1.words == o2.words; } diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp index a6972011f9..d219f3e65f 100644 --- a/test/opt/instruction_test.cpp +++ b/test/opt/instruction_test.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include "gmock/gmock.h" @@ -60,6 +61,18 @@ TEST(InstructionTest, CreateWithOpcodeAndNoOperands) { EXPECT_EQ(inst.end(), inst.begin()); } +TEST(InstructionTest, OperandAsCString) { + Operand::OperandData abcde{0x64636261, 0x65}; + Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde)); + EXPECT_STREQ("abcde", operand.AsCString()); +} + +TEST(InstructionTest, OperandAsString) { + Operand::OperandData abcde{0x64636261, 0x65}; + Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde)); + EXPECT_EQ("abcde", operand.AsString()); +} + // The words for an OpTypeInt for 32-bit signed integer resulting in Id 44. uint32_t kSampleInstructionWords[] = {(4 << 16) | uint32_t(SpvOpTypeInt), 44, 32, 1}; From 7f341ffee6abf0362844e933b7145cd34093d036 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Mon, 23 Mar 2020 10:20:00 -0400 Subject: [PATCH 52/88] Make file formatting comply with POSIX standards (#3242) Add newlines to two files --- examples/cpp-interface/CMakeLists.txt | 2 +- utils/vscode/extension.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cpp-interface/CMakeLists.txt b/examples/cpp-interface/CMakeLists.txt index d050b07597..7887ee7c45 100644 --- a/examples/cpp-interface/CMakeLists.txt +++ b/examples/cpp-interface/CMakeLists.txt @@ -16,4 +16,4 @@ add_spvtools_example( TARGET spirv-tools-cpp-example SRCS main.cpp LIBS SPIRV-Tools-opt -) \ No newline at end of file +) diff --git a/utils/vscode/extension.js b/utils/vscode/extension.js index f2201722db..e7fec28544 100644 --- a/utils/vscode/extension.js +++ b/utils/vscode/extension.js @@ -63,4 +63,4 @@ exports.activate = activate; // this method is called when your extension is deactivated function deactivate() { } -exports.deactivate = deactivate; \ No newline at end of file +exports.deactivate = deactivate; From e6f372c5c2b0143b738818f3c74ba5a932dda978 Mon Sep 17 00:00:00 2001 From: Ehsan Date: Mon, 23 Mar 2020 10:31:05 -0400 Subject: [PATCH 53/88] Whitelist SPV_KHR_ray_tracing (#3241) --- source/opt/aggressive_dead_code_elim_pass.cpp | 1 + source/opt/local_access_chain_convert_pass.cpp | 1 + source/opt/local_single_block_elim_pass.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 2cdf5fff46..db2b67b923 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -931,6 +931,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", }); diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index a8cf94b041..0afe798582 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -379,6 +379,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", }); diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index c66271ef04..b5435bb7a5 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -256,6 +256,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", From 1c8bda3721e6b9302f694b58c26d32eff341b126 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Mon, 23 Mar 2020 11:01:18 -0400 Subject: [PATCH 54/88] Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183) When DebugScope is given in SPIR-V, each instruction following the DebugScope is from the lexical scope pointed by the DebugScope in the high level language. We add DebugScope struction to keep the scope information in Instruction class. When ir_loader loads DebugScope/DebugNoScope, it keeps the scope information in |last_dbg_scope_| and lets following instructions have that scope information. In terms of DebugDeclare/DebugValue, if it is in a function body but outside of a basic block, we keep it in |debug_insts_in_header_| of Function class. If it is in a basic block, we keep it as a normal instruction i.e., in a instruction list of BasicBlock. --- source/opt/function.cpp | 17 ++ source/opt/function.h | 9 + source/opt/instruction.cpp | 67 +++++++- source/opt/instruction.h | 60 ++++++- source/opt/ir_loader.cpp | 140 ++++++++++++---- source/opt/ir_loader.h | 3 + source/opt/module.cpp | 21 ++- source/opt/module.h | 12 +- source/opt/optimizer.cpp | 7 +- test/opt/ir_loader_test.cpp | 316 +++++++++++++++++++++++++++++------- 10 files changed, 556 insertions(+), 96 deletions(-) diff --git a/source/opt/function.cpp b/source/opt/function.cpp index efda68b78a..5d50f37c60 100644 --- a/source/opt/function.cpp +++ b/source/opt/function.cpp @@ -34,6 +34,11 @@ Function* Function::Clone(IRContext* ctx) const { }, true); + for (const auto& i : debug_insts_in_header_) { + clone->AddDebugInstructionInHeader( + std::unique_ptr(i.Clone(ctx))); + } + clone->blocks_.reserve(blocks_.size()); for (const auto& b : blocks_) { std::unique_ptr bb(b->Clone(ctx)); @@ -79,6 +84,12 @@ bool Function::WhileEachInst(const std::function& f, } } + for (auto& di : debug_insts_in_header_) { + if (!di.WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + for (auto& bb : blocks_) { if (!bb->WhileEachInst(f, run_on_debug_line_insts)) { return false; @@ -106,6 +117,12 @@ bool Function::WhileEachInst(const std::function& f, } } + for (const auto& di : debug_insts_in_header_) { + if (!di.WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + for (const auto& bb : blocks_) { if (!static_cast(bb.get())->WhileEachInst( f, run_on_debug_line_insts)) { diff --git a/source/opt/function.h b/source/opt/function.h index 3908568239..f208d8e4d9 100644 --- a/source/opt/function.h +++ b/source/opt/function.h @@ -56,6 +56,8 @@ class Function { // Appends a parameter to this function. inline void AddParameter(std::unique_ptr p); + // Appends a debug instruction in function header to this function. + inline void AddDebugInstructionInHeader(std::unique_ptr p); // Appends a basic block to this function. inline void AddBasicBlock(std::unique_ptr b); // Appends a basic block to this function at the position |ip|. @@ -151,6 +153,8 @@ class Function { std::unique_ptr def_inst_; // All parameters to this function. std::vector> params_; + // All debug instructions in this function's header. + InstructionList debug_insts_in_header_; // All basic blocks inside this function in specification order std::vector> blocks_; // The OpFunctionEnd instruction. @@ -167,6 +171,11 @@ inline void Function::AddParameter(std::unique_ptr p) { params_.emplace_back(std::move(p)); } +inline void Function::AddDebugInstructionInHeader( + std::unique_ptr p) { + debug_insts_in_header_.push_back(std::move(p)); +} + inline void Function::AddBasicBlock(std::unique_ptr b) { AddBasicBlock(std::move(b), end()); } diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 49f91426da..b531181316 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -16,6 +16,7 @@ #include +#include "OpenCLDebugInfo100.h" #include "source/disassemble.h" #include "source/opt/fold.h" #include "source/opt/ir_context.h" @@ -30,6 +31,11 @@ const uint32_t kTypeImageDimIndex = 1; const uint32_t kLoadBaseIndex = 0; const uint32_t kVariableStorageClassIndex = 0; const uint32_t kTypeImageSampledIndex = 5; + +// Constants for OpenCL.DebugInfo.100 extension instructions. +const uint32_t kDebugScopeNumWords = 7; +const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6; +const uint32_t kDebugNoScopeNumWords = 5; } // namespace Instruction::Instruction(IRContext* c) @@ -38,7 +44,8 @@ Instruction::Instruction(IRContext* c) opcode_(SpvOpNop), has_type_id_(false), has_result_id_(false), - unique_id_(c->TakeNextUniqueId()) {} + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} Instruction::Instruction(IRContext* c, SpvOp op) : utils::IntrusiveNodeBase(), @@ -46,7 +53,8 @@ Instruction::Instruction(IRContext* c, SpvOp op) opcode_(op), has_type_id_(false), has_result_id_(false), - unique_id_(c->TakeNextUniqueId()) {} + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, std::vector&& dbg_line) @@ -55,7 +63,8 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, has_type_id_(inst.type_id != 0), has_result_id_(inst.result_id != 0), unique_id_(c->TakeNextUniqueId()), - dbg_line_insts_(std::move(dbg_line)) { + dbg_line_insts_(std::move(dbg_line)), + dbg_scope_(kNoDebugScope, kNoInlinedAt) { assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) && "Op(No)Line attaching to Op(No)Line found"); for (uint32_t i = 0; i < inst.num_operands; ++i) { @@ -67,6 +76,23 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, } } +Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + const DebugScope& dbg_scope) + : context_(c), + opcode_(static_cast(inst.opcode)), + has_type_id_(inst.type_id != 0), + has_result_id_(inst.result_id != 0), + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(dbg_scope) { + for (uint32_t i = 0; i < inst.num_operands; ++i) { + const auto& current_payload = inst.operands[i]; + std::vector words( + inst.words + current_payload.offset, + inst.words + current_payload.offset + current_payload.num_words); + operands_.emplace_back(current_payload.type, std::move(words)); + } +} + Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id, const OperandList& in_operands) : utils::IntrusiveNodeBase(), @@ -75,7 +101,8 @@ Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id, has_type_id_(ty_id != 0), has_result_id_(res_id != 0), unique_id_(c->TakeNextUniqueId()), - operands_() { + operands_(), + dbg_scope_(kNoDebugScope, kNoInlinedAt) { if (has_type_id_) { operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID, std::initializer_list{ty_id}); @@ -94,7 +121,12 @@ Instruction::Instruction(Instruction&& that) has_result_id_(that.has_result_id_), unique_id_(that.unique_id_), operands_(std::move(that.operands_)), - dbg_line_insts_(std::move(that.dbg_line_insts_)) {} + dbg_line_insts_(std::move(that.dbg_line_insts_)), + dbg_scope_(that.dbg_scope_) { + for (auto& i : dbg_line_insts_) { + i.dbg_scope_ = that.dbg_scope_; + } +} Instruction& Instruction::operator=(Instruction&& that) { opcode_ = that.opcode_; @@ -103,6 +135,7 @@ Instruction& Instruction::operator=(Instruction&& that) { unique_id_ = that.unique_id_; operands_ = std::move(that.operands_); dbg_line_insts_ = std::move(that.dbg_line_insts_); + dbg_scope_ = that.dbg_scope_; return *this; } @@ -114,6 +147,7 @@ Instruction* Instruction::Clone(IRContext* c) const { clone->unique_id_ = c->TakeNextUniqueId(); clone->operands_ = operands_; clone->dbg_line_insts_ = dbg_line_insts_; + clone->dbg_scope_ = dbg_scope_; return clone; } @@ -735,5 +769,28 @@ bool Instruction::IsOpcodeSafeToDelete() const { } } +void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id, + uint32_t ext_set, + std::vector* binary) const { + uint32_t num_words = kDebugScopeNumWords; + OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope; + if (GetLexicalScope() == kNoDebugScope) { + num_words = kDebugNoScopeNumWords; + dbg_opcode = OpenCLDebugInfo100DebugNoScope; + } else if (GetInlinedAt() == kNoInlinedAt) { + num_words = kDebugScopeNumWordsWithoutInlinedAt; + } + std::vector operands = { + (num_words << 16) | static_cast(SpvOpExtInst), + type_id, + result_id, + ext_set, + static_cast(dbg_opcode), + }; + binary->insert(binary->end(), operands.begin(), operands.end()); + if (GetLexicalScope() != kNoDebugScope) binary->push_back(GetLexicalScope()); + if (GetInlinedAt() != kNoInlinedAt) binary->push_back(GetInlinedAt()); +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 63dfa87732..a3342c616d 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -32,6 +32,9 @@ #include "source/opt/reflect.h" #include "spirv-tools/libspirv.h" +const uint32_t kNoDebugScope = 0; +const uint32_t kNoInlinedAt = 0; + namespace spvtools { namespace opt { @@ -100,6 +103,44 @@ inline bool operator!=(const Operand& o1, const Operand& o2) { return !(o1 == o2); } +// This structure is used to represent a DebugScope instruction from +// the OpenCL.100.DebugInfo extened instruction set. Note that we can +// ignore the result id of DebugScope instruction because it is not +// used for anything. We do not keep it to reduce the size of +// structure. +// TODO: Let validator check that the result id is not used anywhere. +class DebugScope { + public: + DebugScope(uint32_t lexical_scope, uint32_t inlined_at) + : lexical_scope_(lexical_scope), inlined_at_(inlined_at) {} + + inline bool operator!=(const DebugScope& d) const { + return lexical_scope_ != d.lexical_scope_ || inlined_at_ != d.inlined_at_; + } + + // Accessor functions for |lexical_scope_|. + uint32_t GetLexicalScope() const { return lexical_scope_; } + void SetLexicalScope(uint32_t scope) { lexical_scope_ = scope; } + + // Accessor functions for |inlined_at_|. + uint32_t GetInlinedAt() const { return inlined_at_; } + void SetInlinedAt(uint32_t at) { inlined_at_ = at; } + + // Pushes the binary segments for this DebugScope instruction into + // the back of *|binary|. + void ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set, + std::vector* binary) const; + + private: + // The result id of the lexical scope in which this debug scope is + // contained. The value is kNoDebugScope if there is no scope. + uint32_t lexical_scope_; + + // The result id of DebugInlinedAt if instruction in this debug scope + // is inlined. The value is kNoInlinedAt if it is not inlined. + uint32_t inlined_at_; +}; + // A SPIR-V instruction. It contains the opcode and any additional logical // operand, including the result id (if any) and result type id (if any). It // may also contain line-related debug instruction (OpLine, OpNoLine) directly @@ -120,7 +161,8 @@ class Instruction : public utils::IntrusiveNodeBase { opcode_(SpvOpNop), has_type_id_(false), has_result_id_(false), - unique_id_(0) {} + unique_id_(0), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} // Creates a default OpNop instruction. Instruction(IRContext*); @@ -134,6 +176,9 @@ class Instruction : public utils::IntrusiveNodeBase { Instruction(IRContext* c, const spv_parsed_instruction_t& inst, std::vector&& dbg_line = {}); + Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + const DebugScope& dbg_scope); + // Creates an instruction with the given opcode |op|, type id: |ty_id|, // result id: |res_id| and input operands: |in_operands|. Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id, @@ -230,6 +275,9 @@ class Instruction : public utils::IntrusiveNodeBase { // Sets the result id inline void SetResultId(uint32_t res_id); inline bool HasResultId() const { return has_result_id_; } + // Sets DebugScope. + inline void SetDebugScope(const DebugScope& scope); + inline const DebugScope& GetDebugScope() const { return dbg_scope_; } // Remove the |index|-th operand void RemoveOperand(uint32_t index) { operands_.erase(operands_.begin() + index); @@ -482,6 +530,9 @@ class Instruction : public utils::IntrusiveNodeBase { // empty. std::vector dbg_line_insts_; + // DebugScope that wraps this instruction. + DebugScope dbg_scope_; + friend InstructionList; }; @@ -553,6 +604,13 @@ inline void Instruction::SetResultId(uint32_t res_id) { operands_[ridx].words = {res_id}; } +inline void Instruction::SetDebugScope(const DebugScope& scope) { + dbg_scope_ = scope; + for (auto& i : dbg_line_insts_) { + i.dbg_scope_ = scope; + } +} + inline void Instruction::SetResultType(uint32_t ty_id) { // TODO(dsinclair): Allow setting a type id if there wasn't one // previously. Need to make room in the operands_ array to place the result, diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index 836012f171..fcde0797bd 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -23,6 +23,10 @@ #include "source/opt/reflect.h" #include "source/util/make_unique.h" +static const uint32_t kExtInstSetIndex = 4; +static const uint32_t kLexicalScopeIndex = 5; +static const uint32_t kInlinedAtIndex = 6; + namespace spvtools { namespace opt { @@ -30,16 +34,60 @@ IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) : consumer_(consumer), module_(m), source_(""), - inst_index_(0) {} + inst_index_(0), + last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { ++inst_index_; const auto opcode = static_cast(inst->opcode); if (IsDebugLineInst(opcode)) { - dbg_line_info_.push_back(Instruction(module()->context(), *inst)); + dbg_line_info_.push_back( + Instruction(module()->context(), *inst, last_dbg_scope_)); return true; } + // If it is a DebugScope or DebugNoScope of debug extension, we do not + // create a new instruction, but simply keep the information in + // struct DebugScope. + if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) { + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == OpenCLDebugInfo100DebugScope) { + uint32_t inlined_at = 0; + if (inst->num_words > kInlinedAtIndex) + inlined_at = inst->words[kInlinedAtIndex]; + last_dbg_scope_ = + DebugScope(inst->words[kLexicalScopeIndex], inlined_at); + module()->SetContainsDebugScope(); + return true; + } + if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) { + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + module()->SetContainsDebugScope(); + return true; + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + if (ext_inst_key == DebugInfoDebugScope) { + uint32_t inlined_at = 0; + if (inst->num_words > kInlinedAtIndex) + inlined_at = inst->words[kInlinedAtIndex]; + last_dbg_scope_ = + DebugScope(inst->words[kLexicalScopeIndex], inlined_at); + module()->SetContainsDebugScope(); + return true; + } + if (ext_inst_key == DebugInfoDebugNoScope) { + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + module()->SetContainsDebugScope(); + return true; + } + } + } + std::unique_ptr spv_inst( new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); dbg_line_info_.clear(); @@ -90,6 +138,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { block_->AddInstruction(std::move(spv_inst)); function_->AddBasicBlock(std::move(block_)); block_ = nullptr; + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); } else { if (function_ == nullptr) { // Outside function definition SPIRV_ASSERT(consumer_, block_ == nullptr); @@ -131,26 +180,32 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { return false; } } else { - if (block_ == nullptr) { // Inside function but outside blocks - if (opcode != SpvOpFunctionParameter) { - Errorf(consumer_, src, loc, - "Non-OpFunctionParameter (opcode: %d) found inside " - "function but outside basic block", - opcode); - return false; - } - function_->AddParameter(std::move(spv_inst)); - } else { - if (opcode == SpvOpExtInst && - spvExtInstIsDebugInfo(inst->ext_inst_type)) { - const uint32_t ext_inst_index = inst->words[4]; - if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { - const OpenCLDebugInfo100Instructions ext_inst_key = - OpenCLDebugInfo100Instructions(ext_inst_index); - if (ext_inst_key != OpenCLDebugInfo100DebugScope && - ext_inst_key != OpenCLDebugInfo100DebugNoScope && - ext_inst_key != OpenCLDebugInfo100DebugDeclare && - ext_inst_key != OpenCLDebugInfo100DebugValue) { + if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) + spv_inst->SetDebugScope(last_dbg_scope_); + if (opcode == SpvOpExtInst && + spvExtInstIsDebugInfo(inst->ext_inst_type)) { + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case OpenCLDebugInfo100DebugDeclare: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + case OpenCLDebugInfo100DebugValue: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + default: { Errorf(consumer_, src, loc, "Debug info extension instruction other than DebugScope, " "DebugNoScope, DebugDeclare, and DebugValue found inside " @@ -158,13 +213,26 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { opcode); return false; } - } else { - const DebugInfoInstructions ext_inst_key = - DebugInfoInstructions(ext_inst_index); - if (ext_inst_key != DebugInfoDebugScope && - ext_inst_key != DebugInfoDebugNoScope && - ext_inst_key != DebugInfoDebugDeclare && - ext_inst_key != DebugInfoDebugValue) { + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case DebugInfoDebugDeclare: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + case DebugInfoDebugValue: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + default: { Errorf(consumer_, src, loc, "Debug info extension instruction other than DebugScope, " "DebugNoScope, DebugDeclare, and DebugValue found inside " @@ -174,7 +242,19 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { } } } - block_->AddInstruction(std::move(spv_inst)); + } else { + if (block_ == nullptr) { // Inside function but outside blocks + if (opcode != SpvOpFunctionParameter) { + Errorf(consumer_, src, loc, + "Non-OpFunctionParameter (opcode: %d) found inside " + "function but outside basic block", + opcode); + return false; + } + function_->AddParameter(std::move(spv_inst)); + } else { + block_->AddInstruction(std::move(spv_inst)); + } } } } diff --git a/source/opt/ir_loader.h b/source/opt/ir_loader.h index 940d7b0db0..5079921503 100644 --- a/source/opt/ir_loader.h +++ b/source/opt/ir_loader.h @@ -78,6 +78,9 @@ class IrLoader { std::unique_ptr block_; // Line related debug instructions accumulated thus far. std::vector dbg_line_info_; + + // The last DebugScope information that IrLoader::AddInstruction() handled. + DebugScope last_dbg_scope_; }; } // namespace opt diff --git a/source/opt/module.cpp b/source/opt/module.cpp index 4403894d83..2959d3d9fb 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -137,10 +137,27 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { binary->push_back(header_.bound); binary->push_back(header_.reserved); - auto write_inst = [binary, skip_nop](const Instruction* i) { - if (!(skip_nop && i->IsNop())) i->ToBinaryWithoutAttachedDebugInsts(binary); + size_t bound_idx = binary->size() - 2; + DebugScope last_scope(kNoDebugScope, kNoInlinedAt); + auto write_inst = [binary, skip_nop, &last_scope, + this](const Instruction* i) { + if (!(skip_nop && i->IsNop())) { + const auto& scope = i->GetDebugScope(); + if (scope != last_scope) { + // Emit DebugScope |scope| to |binary|. + auto dbg_inst = ext_inst_debuginfo_.begin(); + scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(), + dbg_inst->GetSingleWordOperand(2), binary); + last_scope = scope; + } + + i->ToBinaryWithoutAttachedDebugInsts(binary); + } }; ForEachInst(write_inst, true); + + // We create new instructions for DebugScope. The bound must be updated. + binary->data()[bound_idx] = header_.bound; } uint32_t Module::ComputeIdBound() const { diff --git a/source/opt/module.h b/source/opt/module.h index fc53d35a1a..2c96f0295a 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -48,7 +49,7 @@ class Module { using const_inst_iterator = InstructionList::const_iterator; // Creates an empty module with zero'd header. - Module() : header_({}) {} + Module() : header_({}), contains_debug_scope_(false) {} // Sets the header to the given |header|. void SetHeader(const ModuleHeader& header) { header_ = header; } @@ -118,6 +119,10 @@ class Module { // Appends a function to this module. inline void AddFunction(std::unique_ptr f); + // Sets |contains_debug_scope_| as true. + inline void SetContainsDebugScope(); + inline bool ContainsDebugScope() { return contains_debug_scope_; } + // Returns a vector of pointers to type-declaration instructions in this // module. std::vector GetTypes(); @@ -295,6 +300,9 @@ class Module { // If the module ends with Op*Line instruction, they will not be attached to // any instruction. We record them here, so they will not be lost. std::vector trailing_dbg_line_info_; + + // This module contains DebugScope or DebugNoScope. + bool contains_debug_scope_; }; // Pretty-prints |module| to |str|. Returns |str|. @@ -356,6 +364,8 @@ inline void Module::AddFunction(std::unique_ptr f) { functions_.emplace_back(std::move(f)); } +inline void Module::SetContainsDebugScope() { contains_debug_scope_ = true; } + inline Module::inst_iterator Module::capability_begin() { return capabilities_.begin(); } diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 6e271f5384..0a937e8577 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -569,7 +569,12 @@ bool Optimizer::Run(const uint32_t* original_binary, } #ifndef NDEBUG - if (status == opt::Pass::Status::SuccessWithoutChange) { + // We do not keep the result id of DebugScope in struct DebugScope. + // Instead, we assign random ids for them, which results in sanity + // check failures. We want to skip the sanity check when the module + // contains DebugScope instructions. + if (status == opt::Pass::Status::SuccessWithoutChange && + !context->module()->ContainsDebugScope()) { std::vector optimized_binary_with_nop; context->module()->ToBinary(&optimized_binary_with_nop, /* skip_nop = */ false); diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp index c60e8537ad..50e3a08f3a 100644 --- a/test/opt/ir_loader_test.cpp +++ b/test/opt/ir_loader_test.cpp @@ -234,34 +234,33 @@ OpLine %7 3 18 %44 = OpExtInst %void %1 DebugLocalVariable %16 %40 %34 6 16 %43 FlagIsLocal 0 %45 = OpExtInst %void %1 DebugLocalVariable %17 %40 %34 7 16 %43 FlagIsLocal 1 %46 = OpExtInst %void %1 DebugLocalVariable %18 %36 %34 8 3 %43 FlagIsLocal -%47 = OpExtInst %void %1 DebugDeclare %44 %pos %42 -%48 = OpExtInst %void %1 DebugDeclare %45 %color %42 OpLine %7 6 1 %main = OpFunction %void None %31 -%49 = OpLabel -%50 = OpExtInst %void %1 DebugScope %43 +%47 = OpLabel +%60 = OpExtInst %void %1 DebugScope %43 OpLine %7 8 13 %vout = OpVariable %_ptr_Function_VS_OUTPUT Function -%51 = OpExtInst %void %1 DebugDeclare %46 %vout %42 +%49 = OpExtInst %void %1 DebugDeclare %46 %vout %42 OpLine %7 9 14 -%52 = OpLoad %v4float %pos +%50 = OpLoad %v4float %pos OpLine %7 9 3 -%53 = OpAccessChain %_ptr_Function_v4float %vout %int_0 -%54 = OpExtInst %void %1 DebugValue %46 %53 %42 %int_0 -OpStore %53 %52 +%51 = OpAccessChain %_ptr_Function_v4float %vout %int_0 +%52 = OpExtInst %void %1 DebugValue %46 %51 %42 %int_0 +OpStore %51 %50 OpLine %7 10 16 -%55 = OpLoad %v4float %color +%53 = OpLoad %v4float %color OpLine %7 10 3 -%56 = OpAccessChain %_ptr_Function_v4float %vout %int_1 -%57 = OpExtInst %void %1 DebugValue %46 %56 %42 %int_1 -OpStore %56 %55 +%54 = OpAccessChain %_ptr_Function_v4float %vout %int_1 +%55 = OpExtInst %void %1 DebugValue %46 %54 %42 %int_1 +OpStore %54 %53 OpLine %7 11 10 -%58 = OpLoad %VS_OUTPUT %vout +%56 = OpLoad %VS_OUTPUT %vout OpLine %7 11 3 -%59 = OpCompositeExtract %v4float %58 0 -OpStore %gl_Position %59 -%60 = OpCompositeExtract %v4float %58 1 -OpStore %out_var_COLOR %60 +%57 = OpCompositeExtract %v4float %56 0 +OpStore %gl_Position %57 +%58 = OpCompositeExtract %v4float %56 1 +OpStore %out_var_COLOR %58 +%61 = OpExtInst %void %1 DebugNoScope OpReturn OpFunctionEnd )"); @@ -377,7 +376,7 @@ OpLine %5 12 37 OpLine %5 12 1 %main = OpFunction %void None %36 %bb_entry = OpLabel -%52 = OpExtInst %void %1 DebugScope %47 +%70 = OpExtInst %void %1 DebugScope %47 OpLine %5 13 16 %param_var_arg1 = OpVariable %_ptr_Function_float Function %53 = OpLoad %float %pos @@ -386,6 +385,7 @@ OpLine %5 13 10 %54 = OpFunctionCall %v4float %func1 %param_var_arg1 OpLine %5 13 3 OpStore %gl_Position %54 +%71 = OpExtInst %void %1 DebugNoScope OpReturn OpFunctionEnd OpLine %5 5 1 @@ -393,41 +393,45 @@ OpLine %5 5 1 OpLine %5 5 20 %arg1 = OpFunctionParameter %_ptr_Function_float %bb_entry_0 = OpLabel -%55 = OpExtInst %void %1 DebugScope %48 +%72 = OpExtInst %void %1 DebugScope %48 OpLine %5 9 16 %param_var_arg2 = OpVariable %_ptr_Function_float Function OpLine %5 6 7 -%56 = OpLoad %float %arg1 +%57 = OpLoad %float %arg1 OpLine %5 6 12 -%57 = OpFOrdGreaterThan %bool %56 %float_1 +%58 = OpFOrdGreaterThan %bool %57 %float_1 OpLine %5 6 17 +%73 = OpExtInst %void %1 DebugNoScope OpSelectionMerge %if_merge None -OpBranchConditional %57 %if_true %if_merge +OpBranchConditional %58 %if_true %if_merge %if_true = OpLabel -%58 = OpExtInst %void %1 DebugScope %50 +%74 = OpExtInst %void %1 DebugScope %50 OpLine %5 7 5 +%75 = OpExtInst %void %1 DebugNoScope OpReturnValue %32 %if_merge = OpLabel -%59 = OpExtInst %void %1 DebugScope %51 +%76 = OpExtInst %void %1 DebugScope %51 OpLine %5 9 16 -%60 = OpLoad %float %arg1 -OpStore %param_var_arg2 %60 +%63 = OpLoad %float %arg1 +OpStore %param_var_arg2 %63 OpLine %5 9 10 -%61 = OpFunctionCall %v4float %func2 %param_var_arg2 +%64 = OpFunctionCall %v4float %func2 %param_var_arg2 OpLine %5 9 3 -OpReturnValue %61 +%77 = OpExtInst %void %1 DebugNoScope +OpReturnValue %64 OpFunctionEnd OpLine %5 1 1 %func2 = OpFunction %v4float None %38 OpLine %5 1 20 %arg2 = OpFunctionParameter %_ptr_Function_float %bb_entry_1 = OpLabel -%62 = OpExtInst %void %1 DebugScope %49 +%78 = OpExtInst %void %1 DebugScope %49 OpLine %5 2 17 -%63 = OpLoad %float %arg2 -%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0 +%67 = OpLoad %float %arg2 +%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0 OpLine %5 2 3 -OpReturnValue %64 +%79 = OpExtInst %void %1 DebugNoScope +OpReturnValue %68 OpFunctionEnd )"); } @@ -547,28 +551,31 @@ OpLine %5 12 37 OpLine %5 12 1 %main = OpFunction %void None %28 %bb_entry = OpLabel -%51 = OpExtInst %void %1 DebugScope %44 %49 +%62 = OpExtInst %void %1 DebugScope %44 %49 OpLine %5 6 7 %52 = OpLoad %float %pos OpLine %5 6 12 %53 = OpFOrdGreaterThan %bool %52 %float_1 OpLine %5 6 17 +%63 = OpExtInst %void %1 DebugNoScope OpSelectionMerge %if_merge None OpBranchConditional %53 %if_true %if_merge %if_true = OpLabel -%54 = OpExtInst %void %1 DebugScope %46 %49 +%64 = OpExtInst %void %1 DebugScope %46 %49 OpLine %5 7 5 OpStore %gl_Position %24 +%65 = OpExtInst %void %1 DebugNoScope OpReturn %if_merge = OpLabel -%55 = OpExtInst %void %1 DebugScope %45 %50 +%66 = OpExtInst %void %1 DebugScope %45 %50 OpLine %5 2 17 -%56 = OpLoad %float %pos +%58 = OpLoad %float %pos OpLine %5 2 10 -%57 = OpCompositeConstruct %v4float %56 %float_0 %float_0 %float_0 -%58 = OpExtInst %void %1 DebugScope %43 +%59 = OpCompositeConstruct %v4float %58 %float_0 %float_0 %float_0 +%67 = OpExtInst %void %1 DebugScope %43 OpLine %5 13 3 -OpStore %gl_Position %57 +OpStore %gl_Position %59 +%68 = OpExtInst %void %1 DebugNoScope OpReturn OpFunctionEnd )"); @@ -683,8 +690,8 @@ OpLine %5 12 37 %51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48 OpLine %5 12 1 %main = OpFunction %void None %36 -%52 = OpExtInst %void %1 DebugScope %47 %bb_entry = OpLabel +%70 = OpExtInst %void %1 DebugScope %47 OpLine %5 13 16 %param_var_arg1 = OpVariable %_ptr_Function_float Function %53 = OpLoad %float %pos @@ -693,6 +700,7 @@ OpLine %5 13 10 %54 = OpFunctionCall %v4float %func1 %param_var_arg1 OpLine %5 13 3 OpStore %gl_Position %54 +%71 = OpExtInst %void %1 DebugNoScope OpReturn OpFunctionEnd OpLine %5 5 1 @@ -700,48 +708,244 @@ OpLine %5 5 1 OpLine %5 5 20 %arg1 = OpFunctionParameter %_ptr_Function_float %bb_entry_0 = OpLabel -%55 = OpExtInst %void %1 DebugScope %48 +%72 = OpExtInst %void %1 DebugScope %48 OpLine %5 9 16 %param_var_arg2 = OpVariable %_ptr_Function_float Function OpLine %5 6 7 -%56 = OpLoad %float %arg1 +%57 = OpLoad %float %arg1 OpLine %5 6 12 -%57 = OpFOrdGreaterThan %bool %56 %float_1 +%58 = OpFOrdGreaterThan %bool %57 %float_1 OpLine %5 6 17 +%73 = OpExtInst %void %1 DebugNoScope OpSelectionMerge %if_merge None -OpBranchConditional %57 %if_true %if_merge +OpBranchConditional %58 %if_true %if_merge %if_true = OpLabel -%58 = OpExtInst %void %1 DebugScope %50 +%74 = OpExtInst %void %1 DebugScope %50 OpLine %5 7 5 +%75 = OpExtInst %void %1 DebugNoScope OpReturnValue %32 %if_merge = OpLabel -%59 = OpExtInst %void %1 DebugScope %51 +%76 = OpExtInst %void %1 DebugScope %51 OpLine %5 9 16 -%60 = OpLoad %float %arg1 -OpStore %param_var_arg2 %60 +%63 = OpLoad %float %arg1 +OpStore %param_var_arg2 %63 OpLine %5 9 10 -%61 = OpFunctionCall %v4float %func2 %param_var_arg2 +%64 = OpFunctionCall %v4float %func2 %param_var_arg2 OpLine %5 9 3 -OpReturnValue %61 +%77 = OpExtInst %void %1 DebugNoScope +OpReturnValue %64 OpFunctionEnd OpLine %5 1 1 %func2 = OpFunction %v4float None %38 OpLine %5 1 20 %arg2 = OpFunctionParameter %_ptr_Function_float %bb_entry_1 = OpLabel -%62 = OpExtInst %void %1 DebugScope %49 +%78 = OpExtInst %void %1 DebugScope %49 OpLine %5 2 17 -%63 = OpLoad %float %arg2 -%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0 +%67 = OpLoad %float %arg2 +%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0 OpLine %5 2 3 -OpReturnValue %64 +%79 = OpExtInst %void %1 DebugNoScope +OpReturnValue %68 OpFunctionEnd )"; SpirvTools t(SPV_ENV_UNIVERSAL_1_1); std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); - ASSERT_EQ(nullptr, context); + ASSERT_NE(nullptr, context); + + std::vector binary; + context->module()->ToBinary(&binary, /* skip_nop = */ false); + + std::string disassembled_text; + EXPECT_TRUE(t.Disassemble(binary, &disassembled_text)); + EXPECT_EQ(text, disassembled_text); +} + +TEST(IrBuilder, DebugInfoInstInFunctionOutOfBlock2) { + // /* HLSL */ + // + // struct VS_OUTPUT { + // float4 pos : SV_POSITION; + // float4 color : COLOR; + // }; + // + // VS_OUTPUT main(float4 pos : POSITION, + // float4 color : COLOR) { + // VS_OUTPUT vout; + // vout.pos = pos; + // vout.color = color; + // return vout; + // } + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR +%7 = OpString "vs.hlsl" +OpSource HLSL 600 %7 "#line 1 \"vs.hlsl\" +struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; + +VS_OUTPUT main(float4 pos : POSITION, + float4 color : COLOR) { + VS_OUTPUT vout; + vout.pos = pos; + vout.color = color; + return vout; +} +" +%8 = OpString "#line 1 \"vs.hlsl\" +struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; + +VS_OUTPUT main(float4 pos : POSITION, + float4 color : COLOR) { + VS_OUTPUT vout; + vout.pos = pos; + vout.color = color; + return vout; +} +" +%9 = OpString "VS_OUTPUT" +%10 = OpString "float" +%11 = OpString "src.main" +%12 = OpString "pos" +%13 = OpString "color" +%14 = OpString "vout" +OpName %in_var_POSITION "in.var.POSITION" +OpName %in_var_COLOR "in.var.COLOR" +OpName %out_var_COLOR "out.var.COLOR" +OpName %main "main" +OpName %param_var_pos "param.var.pos" +OpName %param_var_color "param.var.color" +OpName %VS_OUTPUT "VS_OUTPUT" +OpMemberName %VS_OUTPUT 0 "pos" +OpMemberName %VS_OUTPUT 1 "color" +OpName %src_main "src.main" +OpName %pos "pos" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %vout "vout" +OpDecorate %gl_Position BuiltIn Position +OpDecorate %in_var_POSITION Location 0 +OpDecorate %in_var_COLOR Location 1 +OpDecorate %out_var_COLOR Location 0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_256 = OpConstant %uint 256 +%uint_0 = OpConstant %uint 0 +%uint_128 = OpConstant %uint 128 +%36 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float +%VS_OUTPUT = OpTypeStruct %v4float %v4float +%38 = OpTypeFunction %VS_OUTPUT %_ptr_Function_v4float %_ptr_Function_v4float +%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT +OpLine %7 6 29 +%in_var_POSITION = OpVariable %_ptr_Input_v4float Input +OpLine %7 7 31 +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +OpLine %7 2 16 +%gl_Position = OpVariable %_ptr_Output_v4float Output +OpLine %7 3 18 +%out_var_COLOR = OpVariable %_ptr_Output_v4float Output +%40 = OpExtInst %void %1 DebugExpression +%41 = OpExtInst %void %1 DebugSource %7 %8 +%42 = OpExtInst %void %1 DebugCompilationUnit 1 4 %41 HLSL +%43 = OpExtInst %void %1 DebugTypeComposite %9 Structure %41 1 1 %42 %9 %uint_256 FlagIsProtected|FlagIsPrivate %44 %45 +%46 = OpExtInst %void %1 DebugTypeBasic %10 %uint_32 Float +%47 = OpExtInst %void %1 DebugTypeVector %46 4 +%48 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %47 %47 +%49 = OpExtInst %void %1 DebugFunction %11 %48 %41 6 1 %42 %11 FlagIsProtected|FlagIsPrivate 7 %src_main +%50 = OpExtInst %void %1 DebugLocalVariable %12 %47 %41 6 23 %49 FlagIsLocal 0 +%51 = OpExtInst %void %1 DebugLocalVariable %13 %47 %41 7 23 %49 FlagIsLocal 1 +%52 = OpExtInst %void %1 DebugLexicalBlock %41 7 38 %49 +%53 = OpExtInst %void %1 DebugLocalVariable %14 %43 %41 8 13 %52 FlagIsLocal +%44 = OpExtInst %void %1 DebugTypeMember %12 %47 %41 2 3 %43 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate +%45 = OpExtInst %void %1 DebugTypeMember %13 %47 %41 3 3 %43 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate +OpLine %7 6 1 +%main = OpFunction %void None %36 +%54 = OpLabel +%74 = OpExtInst %void %1 DebugScope %42 +OpLine %7 6 23 +%param_var_pos = OpVariable %_ptr_Function_v4float Function +OpLine %7 7 23 +%param_var_color = OpVariable %_ptr_Function_v4float Function +OpLine %7 6 23 +%56 = OpLoad %v4float %in_var_POSITION +OpStore %param_var_pos %56 +OpLine %7 7 23 +%57 = OpLoad %v4float %in_var_COLOR +OpStore %param_var_color %57 +OpLine %7 6 1 +%58 = OpFunctionCall %VS_OUTPUT %src_main %param_var_pos %param_var_color +OpLine %7 6 11 +%59 = OpCompositeExtract %v4float %58 0 +OpLine %7 2 16 +OpStore %gl_Position %59 +OpLine %7 6 11 +%60 = OpCompositeExtract %v4float %58 1 +OpLine %7 3 18 +OpStore %out_var_COLOR %60 +%75 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +OpLine %7 6 1 +%src_main = OpFunction %VS_OUTPUT None %38 +%76 = OpExtInst %void %1 DebugScope %49 +OpLine %7 6 23 +%pos = OpFunctionParameter %_ptr_Function_v4float +OpLine %7 7 23 +%color = OpFunctionParameter %_ptr_Function_v4float +%63 = OpExtInst %void %1 DebugDeclare %50 %pos %40 +%64 = OpExtInst %void %1 DebugDeclare %51 %color %40 +%77 = OpExtInst %void %1 DebugNoScope +%bb_entry = OpLabel +%78 = OpExtInst %void %1 DebugScope %52 +OpLine %7 8 13 +%vout = OpVariable %_ptr_Function_VS_OUTPUT Function +%67 = OpExtInst %void %1 DebugDeclare %53 %vout %40 +OpLine %7 9 14 +%68 = OpLoad %v4float %pos +OpLine %7 9 3 +%69 = OpAccessChain %_ptr_Function_v4float %vout %int_0 +OpStore %69 %68 +OpLine %7 10 16 +%70 = OpLoad %v4float %color +OpLine %7 10 3 +%71 = OpAccessChain %_ptr_Function_v4float %vout %int_1 +OpStore %71 %70 +OpLine %7 11 10 +%72 = OpLoad %VS_OUTPUT %vout +OpLine %7 11 3 +%79 = OpExtInst %void %1 DebugNoScope +OpReturnValue %72 +OpFunctionEnd +)"; + + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + ASSERT_NE(nullptr, context); + + std::vector binary; + context->module()->ToBinary(&binary, /* skip_nop = */ false); + + std::string disassembled_text; + EXPECT_TRUE(t.Disassemble(binary, &disassembled_text)); + EXPECT_EQ(text, disassembled_text); } TEST(IrBuilder, LocalGlobalVariables) { From 1346dd5de119d603686e260daf08f36958909a23 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Mon, 23 Mar 2020 16:59:37 -0400 Subject: [PATCH 55/88] Disallow phis of images, samplers and sampled images (#3246) * Disallow phis of sampled images always * Disallow phis of samplers and images for shaders * Add tests * Gate check to only occur post-legalization --- source/val/validate_cfg.cpp | 9 ++ test/val/val_cfg_test.cpp | 207 ++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index f3019d17fe..1c279f6549 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -62,6 +62,15 @@ spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) { } } + if (!_.options()->before_hlsl_legalization) { + if (type_opcode == SpvOpTypeSampledImage || + (_.HasCapability(SpvCapabilityShader) && + (type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result type cannot be Op" << spvOpcodeString(type_opcode); + } + } + // Create a uniqued vector of predecessor ids for comparison against // incoming values. OpBranchConditional %cond %label %label produces two // predecessors in the CFG. diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 4cf4029671..0d09642a3f 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -4296,6 +4296,213 @@ TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge2) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateCFG, PhiResultInvalidSampler) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%undef_bool = OpUndef %bool +%undef_sampler = OpUndef %sampler +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type cannot be OpTypeSampler")); +} + +TEST_F(ValidateCFG, PhiResultInvalidImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%undef_bool = OpUndef %bool +%undef_image = OpUndef %image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %image %undef_image %entry %ld_image %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type cannot be OpTypeImage")); +} + +TEST_F(ValidateCFG, PhiResultInvalidSampledImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%sampled_image = OpTypeSampledImage %image +%undef_bool = OpUndef %bool +%undef_sampled_image = OpUndef %sampled_image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop +%sample = OpSampledImage %sampled_image %ld_image %ld_sampler +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type cannot be OpTypeSampledImage")); +} + +TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampler) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%undef_bool = OpUndef %bool +%undef_sampler = OpUndef %sampler +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->before_hlsl_legalization = true; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, PhiResultValidPreLegalizationImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%undef_bool = OpUndef %bool +%undef_image = OpUndef %image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %image %undef_image %entry %ld_image %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->before_hlsl_legalization = true; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampledImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%sampled_image = OpTypeSampledImage %image +%undef_bool = OpUndef %bool +%undef_sampled_image = OpUndef %sampled_image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop +%sample = OpSampledImage %sampled_image %ld_image %ld_sampler +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->before_hlsl_legalization = true; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools From 022da4d0e09de3d707c2e43cd5e8f42ff994b407 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 25 Mar 2020 17:38:24 -0400 Subject: [PATCH 56/88] Fix identification of Vulkan images and buffers (#3253) Fixes #3252 * Image and buffer queries did not account for optional level of arrayness on the variable * new tests --- source/opt/instruction.cpp | 39 +++ test/opt/instruction_test.cpp | 255 ++++++++++++++++++ .../opt/local_redundancy_elimination_test.cpp | 44 +++ 3 files changed, 338 insertions(+) diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index b531181316..3ce38a9a7e 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -232,6 +232,14 @@ bool Instruction::IsVulkanStorageImage() const { Instruction* base_type = context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + if (base_type->opcode() != SpvOpTypeImage) { return false; } @@ -258,6 +266,14 @@ bool Instruction::IsVulkanSampledImage() const { Instruction* base_type = context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + if (base_type->opcode() != SpvOpTypeImage) { return false; } @@ -284,6 +300,14 @@ bool Instruction::IsVulkanStorageTexelBuffer() const { Instruction* base_type = context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + if (base_type->opcode() != SpvOpTypeImage) { return false; } @@ -307,6 +331,13 @@ bool Instruction::IsVulkanStorageBuffer() const { Instruction* base_type = context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + if (base_type->opcode() != SpvOpTypeStruct) { return false; } @@ -340,6 +371,14 @@ bool Instruction::IsVulkanUniformBuffer() const { Instruction* base_type = context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + if (base_type->opcode() != SpvOpTypeStruct) { return false; } diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp index d219f3e65f..1995c5b3d4 100644 --- a/test/opt/instruction_test.cpp +++ b/test/opt/instruction_test.cpp @@ -35,6 +35,7 @@ using DescriptorTypeTest = PassTest<::testing::Test>; using OpaqueTypeTest = PassTest<::testing::Test>; using GetBaseTest = PassTest<::testing::Test>; using ValidBasePointerTest = PassTest<::testing::Test>; +using VulkanBufferTest = PassTest<::testing::Test>; TEST(InstructionTest, CreateTrivial) { Instruction empty; @@ -1143,6 +1144,260 @@ OpFunctionEnd EXPECT_TRUE(null_inst->IsValidBasePointer()); } +TEST_F(VulkanBufferTest, VulkanStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArray +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 1 1 1 +OpDecorate %2 Block +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 BufferBlock +OpMemberDecorate %3 0 Offset 0 +%4 = OpTypeVoid +%5 = OpTypeInt 32 0 +%2 = OpTypeStruct %5 +%3 = OpTypeStruct %5 + +%6 = OpTypePointer StorageBuffer %2 +%7 = OpTypePointer Uniform %2 +%8 = OpTypePointer Uniform %3 + +%9 = OpConstant %5 1 +%10 = OpTypeArray %2 %9 +%11 = OpTypeArray %3 %9 +%12 = OpTypePointer StorageBuffer %10 +%13 = OpTypePointer Uniform %10 +%14 = OpTypePointer Uniform %11 + +%15 = OpTypeRuntimeArray %2 +%16 = OpTypeRuntimeArray %3 +%17 = OpTypePointer StorageBuffer %15 +%18 = OpTypePointer Uniform %15 +%19 = OpTypePointer Uniform %16 + +%50 = OpTypeFunction %4 +%1 = OpFunction %4 None %50 +%51 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + + // Standard SSBO and UBO + Instruction* inst = context->get_def_use_mgr()->GetDef(6); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(7); + EXPECT_EQ(false, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(8); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + + // Arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(12); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(13); + EXPECT_EQ(false, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(14); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + + // Runtime arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(17); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(false, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(19); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); +} + +TEST_F(VulkanBufferTest, VulkanUniformBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArray +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 1 1 1 +OpDecorate %2 Block +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 BufferBlock +OpMemberDecorate %3 0 Offset 0 +%4 = OpTypeVoid +%5 = OpTypeInt 32 0 +%2 = OpTypeStruct %5 +%3 = OpTypeStruct %5 + +%6 = OpTypePointer StorageBuffer %2 +%7 = OpTypePointer Uniform %2 +%8 = OpTypePointer Uniform %3 + +%9 = OpConstant %5 1 +%10 = OpTypeArray %2 %9 +%11 = OpTypeArray %3 %9 +%12 = OpTypePointer StorageBuffer %10 +%13 = OpTypePointer Uniform %10 +%14 = OpTypePointer Uniform %11 + +%15 = OpTypeRuntimeArray %2 +%16 = OpTypeRuntimeArray %3 +%17 = OpTypePointer StorageBuffer %15 +%18 = OpTypePointer Uniform %15 +%19 = OpTypePointer Uniform %16 + +%50 = OpTypeFunction %4 +%1 = OpFunction %4 None %50 +%51 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + + // Standard SSBO and UBO + Instruction* inst = context->get_def_use_mgr()->GetDef(6); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(7); + EXPECT_EQ(true, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(8); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + + // Arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(12); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(13); + EXPECT_EQ(true, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(14); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + + // Runtime arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(17); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(true, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(19); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); +} + +TEST_F(VulkanBufferTest, ImageQueries) { + const std::string text = R"( +OpCapability Shader +OpCapability ImageBuffer +OpCapability RuntimeDescriptorArray +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeFloat 32 + +%4 = OpTypeImage %3 Buffer 0 0 0 1 Rgba32f +%5 = OpTypeImage %3 Buffer 0 0 0 2 Rgba32f +%6 = OpTypeImage %3 2D 0 0 0 1 Rgba32f +%7 = OpTypeImage %3 2D 0 0 0 2 Rgba32f + +%8 = OpTypePointer UniformConstant %4 +%9 = OpTypePointer UniformConstant %5 +%10 = OpTypePointer UniformConstant %6 +%11 = OpTypePointer UniformConstant %7 + +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 1 +%14 = OpTypeArray %4 %13 +%15 = OpTypeArray %5 %13 +%16 = OpTypeArray %6 %13 +%17 = OpTypeArray %7 %13 +%18 = OpTypePointer UniformConstant %14 +%19 = OpTypePointer UniformConstant %15 +%20 = OpTypePointer UniformConstant %16 +%21 = OpTypePointer UniformConstant %17 + +%22 = OpTypeRuntimeArray %4 +%23 = OpTypeRuntimeArray %5 +%24 = OpTypeRuntimeArray %6 +%25 = OpTypeRuntimeArray %7 +%26 = OpTypePointer UniformConstant %22 +%27 = OpTypePointer UniformConstant %23 +%28 = OpTypePointer UniformConstant %24 +%29 = OpTypePointer UniformConstant %25 + +%50 = OpTypeFunction %4 +%1 = OpFunction %4 None %50 +%51 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + + // Bare pointers + Instruction* inst = context->get_def_use_mgr()->GetDef(8); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(9); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(true, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(11); + EXPECT_EQ(true, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + // Array pointers + inst = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(19); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(20); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(true, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(21); + EXPECT_EQ(true, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + // Runtime array pointers + inst = context->get_def_use_mgr()->GetDef(26); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(27); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(28); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(true, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(29); + EXPECT_EQ(true, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/local_redundancy_elimination_test.cpp b/test/opt/local_redundancy_elimination_test.cpp index bc4635e29b..291e1bc258 100644 --- a/test/opt/local_redundancy_elimination_test.cpp +++ b/test/opt/local_redundancy_elimination_test.cpp @@ -154,6 +154,50 @@ TEST_F(LocalRedundancyEliminationTest, KeepInstructionsInDifferentBlocks) { SinglePassRunAndMatch(text, false); } +TEST_F(LocalRedundancyEliminationTest, StorageBufferIdentification) { + const std::string text = R"( +; CHECK: [[gep:%\w+]] = OpAccessChain +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]] +; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] +; CHECK: OpStore [[gep]] [[add]] +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]] +; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] +; CHECK: OpStore [[gep]] [[add]] + +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %block BufferBlock +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%block = OpTypeStruct %int +%array = OpTypeArray %block %int_1 +%ptr_ssbo_array = OpTypePointer Uniform %array +%ptr_ssbo_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_ssbo_array Uniform +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +%ld1 = OpLoad %int %gep1 +%add1 = OpIAdd %int %ld1 %int_1 +%gep2 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +OpStore %gep2 %add1 +%gep3 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +%ld3 = OpLoad %int %gep3 +%add3 = OpIAdd %int %ld3 %int_1 +%gep4 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +OpStore %gep4 %add3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + } // namespace } // namespace opt } // namespace spvtools From 3ef8fe9a521f26c19f1fa42c36a769bef2300ef6 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 26 Mar 2020 02:01:43 -0400 Subject: [PATCH 57/88] Update CHANGES --- CHANGES | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 48c93a4d60..7016881dd7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,31 @@ Revision history for SPIRV-Tools v2020.2-dev 2020-02-03 - - Start v2020.2-dev + - General: + - Support extended instructions in the vscode language server + - Make spvOpcodeString part of the public API (#3174) + - Added guide to writing a spirv-fuzz fuzzer pass (#3190) + - Add support for KHR_ray_{query,tracing} extensions (#3235) + - Optimizer + - Debug Printf support (#3215) + - Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183) + - Fix identification of Vulkan images and buffers (#3253) + - Validator + - Add support for SPV_AMD_shader_image_load_store_lod (#3186) + - Add validation rules for OpenCL.DebugInfo.100 extension (#3133) + - Adding WebGPU specific Workgroup scope rule (#3204) + - Disallow phis of images, samplers and sampled images (#3246) + - Reduce + - Fuzz + - Fuzzer passes to add local and global variables (#3175) + - Add fuzzer passes to add loads/stores (#3176) + - Fuzzer pass to add function calls (#3178) + - Fuzzer pass that adds access chains (#3182) + - Fuzzer pass to add equation instructions (#3202) + - Add swap commutable operands transformation (#3205) + - Add fuzzer pass to permute function parameters (#3212) + - Allow OpPhi operand to be replaced with a composite synonym (#3221) + - Linker v2020.1 2020-02-03 - General: From fd8e130510a6b002b28eee5885a9505040a9bdc9 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 26 Mar 2020 02:02:59 -0400 Subject: [PATCH 58/88] Finalize SPIRV-Tools v2020.2 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 7016881dd7..c47d4f1595 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ Revision history for SPIRV-Tools -v2020.2-dev 2020-02-03 +v2020.2 2020-03-26 - General: - Support extended instructions in the vscode language server - Make spvOpcodeString part of the public API (#3174) From fd773eb50d628c1981338addc093df879757c2cf Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 26 Mar 2020 02:04:07 -0400 Subject: [PATCH 59/88] Start SPIRV-Tools v2020.3 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index c47d4f1595..fe6641ecf0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ Revision history for SPIRV-Tools +v2020.3-dev 2020-03-26 + - Start v2020.3-dev + v2020.2 2020-03-26 - General: - Support extended instructions in the vscode language server From c37c94929bf575f44256469855eaa5aab411f14f Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Tue, 31 Mar 2020 07:06:29 -0700 Subject: [PATCH 60/88] Validate Buffer and BufferBlock apply only to struct types (#3259) --- source/val/validate_decorations.cpp | 20 +++++++++++ test/val/val_capability_test.cpp | 12 ++++--- test/val/val_decoration_test.cpp | 55 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 3b44833378..ce09e180c3 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -1524,6 +1524,22 @@ spv_result_t CheckComponentDecoration(ValidationState_t& vstate, return SPV_SUCCESS; } +// Returns SPV_SUCCESS if validation rules are satisfied for the Block +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckBlockDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + if (inst.opcode() != SpvOpTypeStruct) { + const char* const dec_name = + decoration.dec_type() == SpvDecorationBlock ? "Block" : "BufferBlock"; + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration on a non-struct type."; + } + return SPV_SUCCESS; +} + #define PASS_OR_BAIL_AT_LINE(X, LINE) \ { \ spv_result_t e##LINE = (X); \ @@ -1570,6 +1586,10 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { case SpvDecorationNoUnsignedWrap: PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration)); break; + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration)); + break; default: break; } diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 098fa2f97f..858081812d 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -1229,14 +1229,18 @@ std::make_pair(std::string(kOpenCLMemoryModel) + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + + // Block applies to struct type. "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Block\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %block Block\n" + "%intt = OpTypeInt 32 0\n" + "%block = OpTypeStruct %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + + // BufferBlock applies to struct type. "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt BufferBlock\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %block BufferBlock\n" + "%intt = OpTypeInt 32 0\n" + "%block = OpTypeStruct %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index 256e115f57..ec25d58015 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -700,6 +700,61 @@ TEST_F(ValidateDecorations, RuntimeArrayOfDescriptorSetsIsAllowed) { EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } +TEST_F(ValidateDecorations, BlockDecoratingArrayBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %Output = OpTypeArray %float %int_3 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Block decoration on a non-struct type")); +} + +TEST_F(ValidateDecorations, BlockDecoratingIntBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %Output = OpTypeInt 32 1 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Block decoration on a non-struct type")); +} + TEST_F(ValidateDecorations, BlockMissingOffsetBad) { std::string spirv = R"( OpCapability Shader From f20c0d7971c6705a99b91d7a0278eabbc5d91195 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 1 Apr 2020 12:31:57 -0400 Subject: [PATCH 61/88] Set wrapped kill basic block's parent (#3269) Fixes #3268 * Set the parent of the basic block for the wrapper function * Add a test --- source/opt/wrap_opkill.cpp | 1 + test/opt/wrap_opkill_test.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/source/opt/wrap_opkill.cpp b/source/opt/wrap_opkill.cpp index ffd7a1050d..05e1db0341 100644 --- a/source/opt/wrap_opkill.cpp +++ b/source/opt/wrap_opkill.cpp @@ -147,6 +147,7 @@ uint32_t WrapOpKill::GetOpKillFuncId() { bb->AddInstruction(std::move(kill_inst)); // Add the bb to the function + bb->SetParent(opkill_function_.get()); opkill_function_->AddBasicBlock(std::move(bb)); // Add the function to the module. diff --git a/test/opt/wrap_opkill_test.cpp b/test/opt/wrap_opkill_test.cpp index d50af28a18..b6b6a23210 100644 --- a/test/opt/wrap_opkill_test.cpp +++ b/test/opt/wrap_opkill_test.cpp @@ -513,6 +513,40 @@ OpFunctionEnd EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); } +TEST_F(WrapOpKillTest, SetParentBlock) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %undef %merge %continue +%continue = OpLabel +%call = OpFunctionCall %void %kill_func +OpBranch %loop +%merge = OpLabel +OpReturn +OpFunctionEnd +%kill_func = OpFunction %void None %void_fn +%kill_entry = OpLabel +OpKill +OpFunctionEnd +)"; + + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools From af01d57b5e3c92b3767421d48138a188f836feee Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 2 Apr 2020 08:19:54 -0400 Subject: [PATCH 62/88] Update dominates to check for null nodes (#3271) * Update dominates to check for null nodes Fixes #3270 --- source/opt/dominator_tree.cpp | 1 + .../loop_optimizations/loop_descriptions.cpp | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp index c9346e1c53..da5073a5e0 100644 --- a/source/opt/dominator_tree.cpp +++ b/source/opt/dominator_tree.cpp @@ -241,6 +241,7 @@ bool DominatorTree::Dominates(uint32_t a, uint32_t b) const { bool DominatorTree::Dominates(const DominatorTreeNode* a, const DominatorTreeNode* b) const { + if (!a || !b) return false; // Node A dominates node B if they are the same. if (a == b) return true; diff --git a/test/opt/loop_optimizations/loop_descriptions.cpp b/test/opt/loop_optimizations/loop_descriptions.cpp index 91dbdc6b54..4d2f989a58 100644 --- a/test/opt/loop_optimizations/loop_descriptions.cpp +++ b/test/opt/loop_optimizations/loop_descriptions.cpp @@ -379,6 +379,43 @@ TEST_F(PassClassTest, LoopLatchNotContinue) { EXPECT_EQ(loop.GetLatchBlock()->id(), 30u); } +TEST_F(PassClassTest, UnreachableMerge) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %4 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %6 %7 None + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %7 + %7 = OpLabel + OpBranch %5 + %6 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 1); + LoopDescriptor ld{context.get(), f}; + + EXPECT_EQ(ld.NumLoops(), 1u); +} + } // namespace } // namespace opt } // namespace spvtools From 2fdea57d19d46d565f5517140f60ca87e72f5c8c Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Apr 2020 14:37:59 +0100 Subject: [PATCH 63/88] spirv-fuzz: Add validator options (#3254) Allows several validator options to be passed to the fuzzer, to be used when validation is invoked during fuzzing. --- source/fuzz/fuzzer.cpp | 25 +++++++----- source/fuzz/fuzzer.h | 5 ++- source/fuzz/replayer.cpp | 25 +++++++----- source/fuzz/replayer.h | 3 +- source/fuzz/shrinker.cpp | 35 ++++++++++------- source/fuzz/shrinker.h | 4 +- test/fuzz/fuzzer_replayer_test.cpp | 9 +++-- test/fuzz/fuzzer_shrinker_test.cpp | 28 ++++++++------ tools/fuzz/fuzz.cpp | 62 +++++++++++++++++++++--------- 9 files changed, 126 insertions(+), 70 deletions(-) diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 119bd3c329..3a4fa0eca7 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -86,26 +86,31 @@ void MaybeAddPass( } // namespace struct Fuzzer::Impl { - explicit Impl(spv_target_env env, uint32_t random_seed, - bool validate_after_each_pass) + Impl(spv_target_env env, uint32_t random_seed, bool validate_after_each_pass, + spv_validator_options options) : target_env(env), seed(random_seed), - validate_after_each_fuzzer_pass(validate_after_each_pass) {} + validate_after_each_fuzzer_pass(validate_after_each_pass), + validator_options(options) {} bool ApplyPassAndCheckValidity(FuzzerPass* pass, const opt::IRContext& ir_context, const spvtools::SpirvTools& tools) const; const spv_target_env target_env; // Target environment. + MessageConsumer consumer; // Message consumer. const uint32_t seed; // Seed for random number generator. bool validate_after_each_fuzzer_pass; // Determines whether the validator - // should be invoked after every fuzzer pass. - MessageConsumer consumer; // Message consumer. + // should be invoked after every fuzzer + // pass. + spv_validator_options validator_options; // Options to control validation. }; Fuzzer::Fuzzer(spv_target_env env, uint32_t seed, - bool validate_after_each_fuzzer_pass) - : impl_(MakeUnique(env, seed, validate_after_each_fuzzer_pass)) {} + bool validate_after_each_fuzzer_pass, + spv_validator_options validator_options) + : impl_(MakeUnique(env, seed, validate_after_each_fuzzer_pass, + validator_options)) {} Fuzzer::~Fuzzer() = default; @@ -120,7 +125,8 @@ bool Fuzzer::Impl::ApplyPassAndCheckValidity( if (validate_after_each_fuzzer_pass) { std::vector binary_to_validate; ir_context.module()->ToBinary(&binary_to_validate, false); - if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) { + if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(), + validator_options)) { consumer(SPV_MSG_INFO, nullptr, {}, "Binary became invalid during fuzzing (set a breakpoint to " "inspect); stopping."); @@ -149,7 +155,8 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( } // Initial binary should be valid. - if (!tools.Validate(&binary_in[0], binary_in.size())) { + if (!tools.Validate(&binary_in[0], binary_in.size(), + impl_->validator_options)) { impl_->consumer(SPV_MSG_ERROR, nullptr, {}, "Initial binary is invalid; stopping."); return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid; diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h index 3ac73a1a6f..6c3ef71cca 100644 --- a/source/fuzz/fuzzer.h +++ b/source/fuzz/fuzzer.h @@ -41,8 +41,9 @@ class Fuzzer { // seed for pseudo-random number generation. // |validate_after_each_fuzzer_pass| controls whether the validator will be // invoked after every fuzzer pass is applied. - explicit Fuzzer(spv_target_env env, uint32_t seed, - bool validate_after_each_fuzzer_pass); + Fuzzer(spv_target_env env, uint32_t seed, + bool validate_after_each_fuzzer_pass, + spv_validator_options validator_options); // Disables copy/move constructor/assignment operations. Fuzzer(const Fuzzer&) = delete; diff --git a/source/fuzz/replayer.cpp b/source/fuzz/replayer.cpp index 398ce59501..07dfe72848 100644 --- a/source/fuzz/replayer.cpp +++ b/source/fuzz/replayer.cpp @@ -37,18 +37,22 @@ namespace spvtools { namespace fuzz { struct Replayer::Impl { - explicit Impl(spv_target_env env, bool validate) - : target_env(env), validate_during_replay(validate) {} - - const spv_target_env target_env; // Target environment. - MessageConsumer consumer; // Message consumer. + Impl(spv_target_env env, bool validate, spv_validator_options options) + : target_env(env), + validate_during_replay(validate), + validator_options(options) {} + const spv_target_env target_env; // Target environment. + MessageConsumer consumer; // Message consumer. const bool validate_during_replay; // Controls whether the validator should // be run after every replay step. + spv_validator_options validator_options; // Options to control + // validation }; -Replayer::Replayer(spv_target_env env, bool validate_during_replay) - : impl_(MakeUnique(env, validate_during_replay)) {} +Replayer::Replayer(spv_target_env env, bool validate_during_replay, + spv_validator_options validator_options) + : impl_(MakeUnique(env, validate_during_replay, validator_options)) {} Replayer::~Replayer() = default; @@ -74,7 +78,8 @@ Replayer::ReplayerResultStatus Replayer::Run( } // Initial binary should be valid. - if (!tools.Validate(&binary_in[0], binary_in.size())) { + if (!tools.Validate(&binary_in[0], binary_in.size(), + impl_->validator_options)) { impl_->consumer(SPV_MSG_INFO, nullptr, {}, "Initial binary is invalid; stopping."); return Replayer::ReplayerResultStatus::kInitialBinaryInvalid; @@ -111,8 +116,8 @@ Replayer::ReplayerResultStatus Replayer::Run( ir_context->module()->ToBinary(&binary_to_validate, false); // Check whether the latest transformation led to a valid binary. - if (!tools.Validate(&binary_to_validate[0], - binary_to_validate.size())) { + if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(), + impl_->validator_options)) { impl_->consumer(SPV_MSG_INFO, nullptr, {}, "Binary became invalid during replay (set a " "breakpoint to inspect); stopping."); diff --git a/source/fuzz/replayer.h b/source/fuzz/replayer.h index 1d58baeb7d..e77d8400c3 100644 --- a/source/fuzz/replayer.h +++ b/source/fuzz/replayer.h @@ -37,7 +37,8 @@ class Replayer { }; // Constructs a replayer from the given target environment. - explicit Replayer(spv_target_env env, bool validate_during_replay); + Replayer(spv_target_env env, bool validate_during_replay, + spv_validator_options validator_options); // Disables copy/move constructor/assignment operations. Replayer(const Replayer&) = delete; diff --git a/source/fuzz/shrinker.cpp b/source/fuzz/shrinker.cpp index 1bb92f1007..b8e4145365 100644 --- a/source/fuzz/shrinker.cpp +++ b/source/fuzz/shrinker.cpp @@ -60,20 +60,27 @@ protobufs::TransformationSequence RemoveChunk( } // namespace struct Shrinker::Impl { - explicit Impl(spv_target_env env, uint32_t limit, bool validate) - : target_env(env), step_limit(limit), validate_during_replay(validate) {} - - const spv_target_env target_env; // Target environment. - MessageConsumer consumer; // Message consumer. - const uint32_t step_limit; // Step limit for reductions. - const bool validate_during_replay; // Determines whether to check for - // validity during the replaying of - // transformations. + Impl(spv_target_env env, uint32_t limit, bool validate, + spv_validator_options options) + : target_env(env), + step_limit(limit), + validate_during_replay(validate), + validator_options(options) {} + + const spv_target_env target_env; // Target environment. + MessageConsumer consumer; // Message consumer. + const uint32_t step_limit; // Step limit for reductions. + const bool validate_during_replay; // Determines whether to check for + // validity during the replaying of + // transformations. + spv_validator_options validator_options; // Options to control validation. }; Shrinker::Shrinker(spv_target_env env, uint32_t step_limit, - bool validate_during_replay) - : impl_(MakeUnique(env, step_limit, validate_during_replay)) {} + bool validate_during_replay, + spv_validator_options validator_options) + : impl_(MakeUnique(env, step_limit, validate_during_replay, + validator_options)) {} Shrinker::~Shrinker() = default; @@ -113,7 +120,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run( // succeeds, (b) get the binary that results from running these // transformations, and (c) get the subsequence of the initial transformations // that actually apply (in principle this could be a strict subsequence). - if (Replayer(impl_->target_env, impl_->validate_during_replay) + if (Replayer(impl_->target_env, impl_->validate_during_replay, + impl_->validator_options) .Run(binary_in, initial_facts, transformation_sequence_in, ¤t_best_binary, ¤t_best_transformations) != Replayer::ReplayerResultStatus::kComplete) { @@ -184,7 +192,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run( // transformations inapplicable. std::vector next_binary; protobufs::TransformationSequence next_transformation_sequence; - if (Replayer(impl_->target_env, false) + if (Replayer(impl_->target_env, impl_->validate_during_replay, + impl_->validator_options) .Run(binary_in, initial_facts, transformations_with_chunk_removed, &next_binary, &next_transformation_sequence) != Replayer::ReplayerResultStatus::kComplete) { diff --git a/source/fuzz/shrinker.h b/source/fuzz/shrinker.h index 0163a53abd..17b15bf8c1 100644 --- a/source/fuzz/shrinker.h +++ b/source/fuzz/shrinker.h @@ -50,8 +50,8 @@ class Shrinker { const std::vector& binary, uint32_t counter)>; // Constructs a shrinker from the given target environment. - Shrinker(spv_target_env env, uint32_t step_limit, - bool validate_during_replay); + Shrinker(spv_target_env env, uint32_t step_limit, bool validate_during_replay, + spv_validator_options validator_options); // Disables copy/move constructor/assignment operations. Shrinker(const Shrinker&) = delete; diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index b91393ef87..b9024b9dbf 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -1602,8 +1602,9 @@ void RunFuzzerAndReplayer(const std::string& shader, std::vector fuzzer_binary_out; protobufs::TransformationSequence fuzzer_transformation_sequence_out; - Fuzzer fuzzer(env, seed, true); - fuzzer.SetMessageConsumer(kSilentConsumer); + spvtools::ValidatorOptions validator_options; + Fuzzer fuzzer(env, seed, true, validator_options); + fuzzer.SetMessageConsumer(kConsoleMessageConsumer); auto fuzzer_result_status = fuzzer.Run(binary_in, initial_facts, donor_suppliers, &fuzzer_binary_out, &fuzzer_transformation_sequence_out); @@ -1613,8 +1614,8 @@ void RunFuzzerAndReplayer(const std::string& shader, std::vector replayer_binary_out; protobufs::TransformationSequence replayer_transformation_sequence_out; - Replayer replayer(env, false); - replayer.SetMessageConsumer(kSilentConsumer); + Replayer replayer(env, false, validator_options); + replayer.SetMessageConsumer(kConsoleMessageConsumer); auto replayer_result_status = replayer.Run( binary_in, initial_facts, fuzzer_transformation_sequence_out, &replayer_binary_out, &replayer_transformation_sequence_out); diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp index c906a1e519..24b44602ec 100644 --- a/test/fuzz/fuzzer_shrinker_test.cpp +++ b/test/fuzz/fuzzer_shrinker_test.cpp @@ -979,15 +979,19 @@ class InterestingThenRandom : public InterestingnessTest { // The |step_limit| parameter restricts the number of steps that the shrinker // will try; it can be set to something small for a faster (but less thorough) // test. +// +// The |validator_options| parameter provides validator options that should be +// used during shrinking. void RunAndCheckShrinker( const spv_target_env& target_env, const std::vector& binary_in, const protobufs::FactSequence& initial_facts, const protobufs::TransformationSequence& transformation_sequence_in, const Shrinker::InterestingnessFunction& interestingness_function, const std::vector& expected_binary_out, - uint32_t expected_transformations_out_size, uint32_t step_limit) { + uint32_t expected_transformations_out_size, uint32_t step_limit, + spv_validator_options validator_options) { // Run the shrinker. - Shrinker shrinker(target_env, step_limit, false); + Shrinker shrinker(target_env, step_limit, false, validator_options); shrinker.SetMessageConsumer(kSilentConsumer); std::vector binary_out; @@ -1035,7 +1039,8 @@ void RunFuzzerAndShrinker(const std::string& shader, // Run the fuzzer and check that it successfully yields a valid binary. std::vector fuzzer_binary_out; protobufs::TransformationSequence fuzzer_transformation_sequence_out; - Fuzzer fuzzer(env, seed, true); + spvtools::ValidatorOptions validator_options; + Fuzzer fuzzer(env, seed, true, validator_options); fuzzer.SetMessageConsumer(kSilentConsumer); auto fuzzer_result_status = fuzzer.Run(binary_in, initial_facts, donor_suppliers, &fuzzer_binary_out, @@ -1048,9 +1053,10 @@ void RunFuzzerAndShrinker(const std::string& shader, // With the AlwaysInteresting test, we should quickly shrink to the original // binary with no transformations remaining. - RunAndCheckShrinker( - env, binary_in, initial_facts, fuzzer_transformation_sequence_out, - AlwaysInteresting().AsFunction(), binary_in, 0, kReasonableStepLimit); + RunAndCheckShrinker(env, binary_in, initial_facts, + fuzzer_transformation_sequence_out, + AlwaysInteresting().AsFunction(), binary_in, 0, + kReasonableStepLimit, validator_options); // With the OnlyInterestingFirstTime test, no shrinking should be achieved. RunAndCheckShrinker( @@ -1058,14 +1064,14 @@ void RunFuzzerAndShrinker(const std::string& shader, OnlyInterestingFirstTime().AsFunction(), fuzzer_binary_out, static_cast( fuzzer_transformation_sequence_out.transformation_size()), - kReasonableStepLimit); + kReasonableStepLimit, validator_options); // The PingPong test is unpredictable; passing an empty expected binary // means that we don't check anything beyond that shrinking completes // successfully. - RunAndCheckShrinker(env, binary_in, initial_facts, - fuzzer_transformation_sequence_out, - PingPong().AsFunction(), {}, 0, kSmallStepLimit); + RunAndCheckShrinker( + env, binary_in, initial_facts, fuzzer_transformation_sequence_out, + PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options); // The InterestingThenRandom test is unpredictable; passing an empty // expected binary means that we do not check anything about shrinking @@ -1073,7 +1079,7 @@ void RunFuzzerAndShrinker(const std::string& shader, RunAndCheckShrinker( env, binary_in, initial_facts, fuzzer_transformation_sequence_out, InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0, - kSmallStepLimit); + kSmallStepLimit, validator_options); } TEST(FuzzerShrinkerTest, Miscellaneous1) { diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index 718d038a80..469c81b48a 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp @@ -145,6 +145,13 @@ Options (in lexicographical order): --version Display fuzzer version information. +Supported validator options are as follows. See `spirv-val --help` for details. + --before-hlsl-legalization + --relax-block-layout + --relax-logical-pointer + --relax-struct-store + --scalar-block-layout + --skip-block-layout )", program, program, program, program); } @@ -166,7 +173,8 @@ FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file, std::vector* interestingness_test, std::string* shrink_transformations_file, std::string* shrink_temp_file_prefix, - spvtools::FuzzerOptions* fuzzer_options) { + spvtools::FuzzerOptions* fuzzer_options, + spvtools::ValidatorOptions* validator_options) { uint32_t positional_arg_index = 0; bool only_positional_arguments_remain = false; bool force_render_red = false; @@ -227,6 +235,18 @@ FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file, sizeof("--shrinker-temp-file-prefix=") - 1)) { const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); *shrink_temp_file_prefix = std::string(split_flag.second); + } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) { + validator_options->SetBeforeHlslLegalization(true); + } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) { + validator_options->SetRelaxLogicalPointer(true); + } else if (0 == strcmp(cur_arg, "--relax-block-layout")) { + validator_options->SetRelaxBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) { + validator_options->SetScalarBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { + validator_options->SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { + validator_options->SetRelaxStructStore(true); } else if (0 == strcmp(cur_arg, "--")) { only_positional_arguments_remain = true; } else { @@ -357,6 +377,7 @@ bool ParseTransformations( bool Replay(const spv_target_env& target_env, spv_const_fuzzer_options fuzzer_options, + spv_validator_options validator_options, const std::vector& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, const std::string& replay_transformations_file, @@ -368,8 +389,8 @@ bool Replay(const spv_target_env& target_env, &transformation_sequence)) { return false; } - spvtools::fuzz::Replayer replayer(target_env, - fuzzer_options->replay_validation_enabled); + spvtools::fuzz::Replayer replayer( + target_env, fuzzer_options->replay_validation_enabled, validator_options); replayer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); auto replay_result_status = replayer.Run(binary_in, initial_facts, transformation_sequence, @@ -380,6 +401,7 @@ bool Replay(const spv_target_env& target_env, bool Shrink(const spv_target_env& target_env, spv_const_fuzzer_options fuzzer_options, + spv_validator_options validator_options, const std::vector& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, const std::string& shrink_transformations_file, @@ -393,9 +415,9 @@ bool Shrink(const spv_target_env& target_env, &transformation_sequence)) { return false; } - spvtools::fuzz::Shrinker shrinker(target_env, - fuzzer_options->shrinker_step_limit, - fuzzer_options->replay_validation_enabled); + spvtools::fuzz::Shrinker shrinker( + target_env, fuzzer_options->shrinker_step_limit, + fuzzer_options->replay_validation_enabled, validator_options); shrinker.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); assert(!interestingness_command.empty() && @@ -434,6 +456,7 @@ bool Shrink(const spv_target_env& target_env, bool Fuzz(const spv_target_env& target_env, spv_const_fuzzer_options fuzzer_options, + spv_validator_options validator_options, const std::vector& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, const std::string& donors, std::vector* binary_out, @@ -469,7 +492,7 @@ bool Fuzz(const spv_target_env& target_env, fuzzer_options->has_random_seed ? fuzzer_options->random_seed : static_cast(std::random_device()()), - fuzzer_options->fuzzer_pass_validation_enabled); + fuzzer_options->fuzzer_pass_validation_enabled, validator_options); fuzzer.SetMessageConsumer(message_consumer); auto fuzz_result_status = fuzzer.Run(binary_in, initial_facts, donor_suppliers, binary_out, @@ -513,11 +536,13 @@ int main(int argc, const char** argv) { std::string shrink_temp_file_prefix = "temp_"; spvtools::FuzzerOptions fuzzer_options; + spvtools::ValidatorOptions validator_options; - FuzzStatus status = ParseFlags( - argc, argv, &in_binary_file, &out_binary_file, &donors_file, - &replay_transformations_file, &interestingness_test, - &shrink_transformations_file, &shrink_temp_file_prefix, &fuzzer_options); + FuzzStatus status = + ParseFlags(argc, argv, &in_binary_file, &out_binary_file, &donors_file, + &replay_transformations_file, &interestingness_test, + &shrink_transformations_file, &shrink_temp_file_prefix, + &fuzzer_options, &validator_options); if (status.action == FuzzActions::STOP) { return status.code; @@ -561,14 +586,15 @@ int main(int argc, const char** argv) { } break; case FuzzActions::FUZZ: - if (!Fuzz(target_env, fuzzer_options, binary_in, initial_facts, - donors_file, &binary_out, &transformations_applied)) { + if (!Fuzz(target_env, fuzzer_options, validator_options, binary_in, + initial_facts, donors_file, &binary_out, + &transformations_applied)) { return 1; } break; case FuzzActions::REPLAY: - if (!Replay(target_env, fuzzer_options, binary_in, initial_facts, - replay_transformations_file, &binary_out, + if (!Replay(target_env, fuzzer_options, validator_options, binary_in, + initial_facts, replay_transformations_file, &binary_out, &transformations_applied)) { return 1; } @@ -579,9 +605,9 @@ int main(int argc, const char** argv) { << std::endl; return 1; } - if (!Shrink(target_env, fuzzer_options, binary_in, initial_facts, - shrink_transformations_file, shrink_temp_file_prefix, - interestingness_test, &binary_out, + if (!Shrink(target_env, fuzzer_options, validator_options, binary_in, + initial_facts, shrink_transformations_file, + shrink_temp_file_prefix, interestingness_test, &binary_out, &transformations_applied)) { return 1; } From 8d4261bc4401fb7c40542df74c81e9506b054244 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Apr 2020 15:54:46 +0100 Subject: [PATCH 64/88] spirv-fuzz: Introduce TransformationContext (#3272) Some transformations (e.g. TransformationAddFunction) rely on running the validator to decide whether the transformation is applicable. A recent change allowed spirv-fuzz to take validator options, to cater for the case where a module should be considered valid under particular conditions. However, validation during the checking of transformations had no access to these validator options. This change introduced TransformationContext, which currently consists of a fact manager and a set of validator options, but could in the future have other fields corresponding to other objects that it is useful to have access to when applying transformations. Now, instead of checking and applying transformations in the context of a FactManager, a TransformationContext is used. This gives access to the fact manager as before, and also access to the validator options when they are needed. --- source/fuzz/CMakeLists.txt | 2 + source/fuzz/force_render_red.cpp | 13 +- source/fuzz/force_render_red.h | 3 +- source/fuzz/fuzzer.cpp | 145 +++--- source/fuzz/fuzzer_pass.cpp | 5 +- source/fuzz/fuzzer_pass.h | 22 +- source/fuzz/fuzzer_pass_add_access_chains.cpp | 5 +- source/fuzz/fuzzer_pass_add_access_chains.h | 2 +- .../fuzz/fuzzer_pass_add_composite_types.cpp | 5 +- source/fuzz/fuzzer_pass_add_composite_types.h | 2 +- source/fuzz/fuzzer_pass_add_dead_blocks.cpp | 10 +- source/fuzz/fuzzer_pass_add_dead_blocks.h | 3 +- source/fuzz/fuzzer_pass_add_dead_breaks.cpp | 14 +- source/fuzz/fuzzer_pass_add_dead_breaks.h | 3 +- .../fuzz/fuzzer_pass_add_dead_continues.cpp | 10 +- source/fuzz/fuzzer_pass_add_dead_continues.h | 2 +- .../fuzzer_pass_add_equation_instructions.cpp | 5 +- .../fuzzer_pass_add_equation_instructions.h | 2 +- .../fuzz/fuzzer_pass_add_function_calls.cpp | 18 +- source/fuzz/fuzzer_pass_add_function_calls.h | 2 +- .../fuzz/fuzzer_pass_add_global_variables.cpp | 5 +- .../fuzz/fuzzer_pass_add_global_variables.h | 2 +- source/fuzz/fuzzer_pass_add_loads.cpp | 5 +- source/fuzz/fuzzer_pass_add_loads.h | 3 +- .../fuzz/fuzzer_pass_add_local_variables.cpp | 5 +- source/fuzz/fuzzer_pass_add_local_variables.h | 2 +- ...er_pass_add_no_contraction_decorations.cpp | 5 +- ...zzer_pass_add_no_contraction_decorations.h | 2 +- source/fuzz/fuzzer_pass_add_stores.cpp | 15 +- source/fuzz/fuzzer_pass_add_stores.h | 3 +- .../fuzzer_pass_add_useful_constructs.cpp | 44 +- .../fuzz/fuzzer_pass_add_useful_constructs.h | 2 +- .../fuzzer_pass_adjust_function_controls.cpp | 5 +- .../fuzzer_pass_adjust_function_controls.h | 2 +- .../fuzz/fuzzer_pass_adjust_loop_controls.cpp | 5 +- .../fuzz/fuzzer_pass_adjust_loop_controls.h | 2 +- ...zzer_pass_adjust_memory_operands_masks.cpp | 5 +- ...fuzzer_pass_adjust_memory_operands_masks.h | 2 +- .../fuzzer_pass_adjust_selection_controls.cpp | 5 +- .../fuzzer_pass_adjust_selection_controls.h | 2 +- source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 12 +- source/fuzz/fuzzer_pass_apply_id_synonyms.h | 2 +- .../fuzz/fuzzer_pass_construct_composites.cpp | 10 +- .../fuzz/fuzzer_pass_construct_composites.h | 2 +- source/fuzz/fuzzer_pass_copy_objects.cpp | 5 +- source/fuzz/fuzzer_pass_copy_objects.h | 3 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 9 +- source/fuzz/fuzzer_pass_donate_modules.h | 2 +- source/fuzz/fuzzer_pass_merge_blocks.cpp | 13 +- source/fuzz/fuzzer_pass_merge_blocks.h | 3 +- .../fuzz/fuzzer_pass_obfuscate_constants.cpp | 35 +- source/fuzz/fuzzer_pass_obfuscate_constants.h | 2 +- source/fuzz/fuzzer_pass_outline_functions.cpp | 10 +- source/fuzz/fuzzer_pass_outline_functions.h | 2 +- source/fuzz/fuzzer_pass_permute_blocks.cpp | 10 +- source/fuzz/fuzzer_pass_permute_blocks.h | 3 +- ...uzzer_pass_permute_function_parameters.cpp | 5 +- .../fuzzer_pass_permute_function_parameters.h | 2 +- source/fuzz/fuzzer_pass_split_blocks.cpp | 10 +- source/fuzz/fuzzer_pass_split_blocks.h | 3 +- .../fuzzer_pass_swap_commutable_operands.cpp | 5 +- .../fuzzer_pass_swap_commutable_operands.h | 2 +- ...r_pass_toggle_access_chain_instruction.cpp | 5 +- ...zer_pass_toggle_access_chain_instruction.h | 2 +- source/fuzz/fuzzer_util.cpp | 4 +- source/fuzz/fuzzer_util.h | 5 +- source/fuzz/replayer.cpp | 8 +- source/fuzz/transformation.cpp | 4 +- source/fuzz/transformation.h | 27 +- source/fuzz/transformation_access_chain.cpp | 56 ++- source/fuzz/transformation_access_chain.h | 19 +- .../transformation_add_constant_boolean.cpp | 19 +- .../transformation_add_constant_boolean.h | 10 +- .../transformation_add_constant_composite.cpp | 24 +- .../transformation_add_constant_composite.h | 10 +- .../transformation_add_constant_scalar.cpp | 20 +- .../fuzz/transformation_add_constant_scalar.h | 10 +- source/fuzz/transformation_add_dead_block.cpp | 42 +- source/fuzz/transformation_add_dead_block.h | 10 +- source/fuzz/transformation_add_dead_break.cpp | 51 +- source/fuzz/transformation_add_dead_break.h | 14 +- .../fuzz/transformation_add_dead_continue.cpp | 53 +- .../fuzz/transformation_add_dead_continue.h | 12 +- source/fuzz/transformation_add_function.cpp | 205 ++++---- source/fuzz/transformation_add_function.h | 41 +- .../fuzz/transformation_add_global_undef.cpp | 18 +- source/fuzz/transformation_add_global_undef.h | 10 +- .../transformation_add_global_variable.cpp | 34 +- .../fuzz/transformation_add_global_variable.h | 16 +- .../transformation_add_local_variable.cpp | 25 +- .../fuzz/transformation_add_local_variable.h | 14 +- ...ormation_add_no_contraction_decoration.cpp | 11 +- ...sformation_add_no_contraction_decoration.h | 10 +- source/fuzz/transformation_add_type_array.cpp | 20 +- source/fuzz/transformation_add_type_array.h | 10 +- .../fuzz/transformation_add_type_boolean.cpp | 18 +- source/fuzz/transformation_add_type_boolean.h | 9 +- source/fuzz/transformation_add_type_float.cpp | 18 +- source/fuzz/transformation_add_type_float.h | 10 +- .../fuzz/transformation_add_type_function.cpp | 22 +- .../fuzz/transformation_add_type_function.h | 10 +- source/fuzz/transformation_add_type_int.cpp | 20 +- source/fuzz/transformation_add_type_int.h | 10 +- .../fuzz/transformation_add_type_matrix.cpp | 18 +- source/fuzz/transformation_add_type_matrix.h | 10 +- .../fuzz/transformation_add_type_pointer.cpp | 19 +- source/fuzz/transformation_add_type_pointer.h | 10 +- .../fuzz/transformation_add_type_struct.cpp | 18 +- source/fuzz/transformation_add_type_struct.h | 10 +- .../fuzz/transformation_add_type_vector.cpp | 18 +- source/fuzz/transformation_add_type_vector.h | 10 +- .../transformation_composite_construct.cpp | 88 ++-- .../fuzz/transformation_composite_construct.h | 21 +- .../fuzz/transformation_composite_extract.cpp | 43 +- .../fuzz/transformation_composite_extract.h | 10 +- source/fuzz/transformation_context.cpp | 29 ++ source/fuzz/transformation_context.h | 56 +++ source/fuzz/transformation_copy_object.cpp | 42 +- source/fuzz/transformation_copy_object.h | 16 +- .../transformation_equation_instruction.cpp | 48 +- .../transformation_equation_instruction.h | 12 +- source/fuzz/transformation_function_call.cpp | 50 +- source/fuzz/transformation_function_call.h | 10 +- source/fuzz/transformation_load.cpp | 27 +- source/fuzz/transformation_load.h | 10 +- source/fuzz/transformation_merge_blocks.cpp | 29 +- source/fuzz/transformation_merge_blocks.h | 10 +- .../fuzz/transformation_move_block_down.cpp | 16 +- source/fuzz/transformation_move_block_down.h | 10 +- .../fuzz/transformation_outline_function.cpp | 263 +++++----- source/fuzz/transformation_outline_function.h | 38 +- ...sformation_permute_function_parameters.cpp | 19 +- ...ansformation_permute_function_parameters.h | 10 +- ..._boolean_constant_with_constant_binary.cpp | 34 +- ...ce_boolean_constant_with_constant_binary.h | 15 +- ...ormation_replace_constant_with_uniform.cpp | 73 +-- ...sformation_replace_constant_with_uniform.h | 12 +- ...transformation_replace_id_with_synonym.cpp | 41 +- .../transformation_replace_id_with_synonym.h | 12 +- .../transformation_set_function_control.cpp | 14 +- .../transformation_set_function_control.h | 13 +- .../fuzz/transformation_set_loop_control.cpp | 21 +- source/fuzz/transformation_set_loop_control.h | 16 +- ...ransformation_set_memory_operands_mask.cpp | 15 +- .../transformation_set_memory_operands_mask.h | 13 +- .../transformation_set_selection_control.cpp | 10 +- .../transformation_set_selection_control.h | 10 +- source/fuzz/transformation_split_block.cpp | 33 +- source/fuzz/transformation_split_block.h | 10 +- source/fuzz/transformation_store.cpp | 33 +- source/fuzz/transformation_store.h | 10 +- ...ransformation_swap_commutable_operands.cpp | 8 +- .../transformation_swap_commutable_operands.h | 10 +- ...mation_toggle_access_chain_instruction.cpp | 8 +- ...ormation_toggle_access_chain_instruction.h | 10 +- source/fuzz/transformation_vector_shuffle.cpp | 62 +-- source/fuzz/transformation_vector_shuffle.h | 17 +- .../fuzz/data_synonym_transformation_test.cpp | 385 ++++++++------ ...fuzzer_pass_add_useful_constructs_test.cpp | 12 +- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 45 +- .../fuzz/transformation_access_chain_test.cpp | 128 +++-- ...ansformation_add_constant_boolean_test.cpp | 38 +- ...sformation_add_constant_composite_test.cpp | 16 +- ...ransformation_add_constant_scalar_test.cpp | 58 ++- .../transformation_add_dead_block_test.cpp | 46 +- .../transformation_add_dead_break_test.cpp | 470 +++++++++++------- .../transformation_add_dead_continue_test.cpp | 158 ++++-- .../fuzz/transformation_add_function_test.cpp | 278 +++++++---- .../transformation_add_global_undef_test.cpp | 20 +- ...ransformation_add_global_variable_test.cpp | 63 ++- ...transformation_add_local_variable_test.cpp | 57 ++- ...ion_add_no_contraction_decoration_test.cpp | 14 +- .../transformation_add_type_array_test.cpp | 24 +- .../transformation_add_type_boolean_test.cpp | 16 +- .../transformation_add_type_float_test.cpp | 16 +- .../transformation_add_type_function_test.cpp | 16 +- .../fuzz/transformation_add_type_int_test.cpp | 24 +- .../transformation_add_type_matrix_test.cpp | 16 +- .../transformation_add_type_pointer_test.cpp | 20 +- .../transformation_add_type_struct_test.cpp | 16 +- .../transformation_add_type_vector_test.cpp | 14 +- ...ransformation_composite_construct_test.cpp | 364 +++++++------- .../transformation_composite_extract_test.cpp | 104 ++-- test/fuzz/transformation_copy_object_test.cpp | 170 ++++--- ...ansformation_equation_instruction_test.cpp | 151 +++--- .../transformation_function_call_test.cpp | 115 +++-- test/fuzz/transformation_load_test.cpp | 62 ++- .../fuzz/transformation_merge_blocks_test.cpp | 83 +++- .../transformation_move_block_down_test.cpp | 346 +++++++------ .../transformation_outline_function_test.cpp | 313 +++++++++--- ...ation_permute_function_parameters_test.cpp | 29 +- ...ean_constant_with_constant_binary_test.cpp | 74 +-- ...ion_replace_constant_with_uniform_test.cpp | 312 +++++++----- ...formation_replace_id_with_synonym_test.cpp | 243 +++++---- ...ansformation_set_function_control_test.cpp | 29 +- .../transformation_set_loop_control_test.cpp | 224 +++++---- ...ormation_set_memory_operands_mask_test.cpp | 79 +-- ...nsformation_set_selection_control_test.cpp | 29 +- test/fuzz/transformation_split_block_test.cpp | 95 ++-- test/fuzz/transformation_store_test.cpp | 87 ++-- ...ormation_swap_commutable_operands_test.cpp | 92 ++-- ...n_toggle_access_chain_instruction_test.cpp | 62 ++- .../transformation_vector_shuffle_test.cpp | 309 ++++++------ tools/fuzz/fuzz.cpp | 3 +- 204 files changed, 4905 insertions(+), 3370 deletions(-) create mode 100644 source/fuzz/transformation_context.cpp create mode 100644 source/fuzz/transformation_context.h diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 3a9d604c84..cd53aea825 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -99,6 +99,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_type_vector.h transformation_composite_construct.h transformation_composite_extract.h + transformation_context.h transformation_copy_object.h transformation_equation_instruction.h transformation_function_call.h @@ -190,6 +191,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_type_vector.cpp transformation_composite_construct.cpp transformation_composite_extract.cpp + transformation_context.cpp transformation_copy_object.cpp transformation_equation_instruction.cpp transformation_function_call.cpp diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp index 46e23e80e6..5bf2879804 100644 --- a/source/fuzz/force_render_red.cpp +++ b/source/fuzz/force_render_red.cpp @@ -17,6 +17,7 @@ #include "source/fuzz/fact_manager.h" #include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" #include "source/fuzz/transformation_replace_constant_with_uniform.h" #include "source/fuzz/uniform_buffer_element_descriptor.h" #include "source/opt/build_module.h" @@ -159,7 +160,8 @@ MakeConstantUniformReplacement(opt::IRContext* ir_context, } // namespace bool ForceRenderRed( - const spv_target_env& target_env, const std::vector& binary_in, + const spv_target_env& target_env, spv_validator_options validator_options, + const std::vector& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, std::vector* binary_out) { auto message_consumer = spvtools::utils::CLIMessageConsumer; @@ -171,7 +173,7 @@ bool ForceRenderRed( } // Initial binary should be valid. - if (!tools.Validate(&binary_in[0], binary_in.size())) { + if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options)) { message_consumer(SPV_MSG_ERROR, nullptr, {}, "Initial binary is invalid; stopping."); return false; @@ -187,6 +189,8 @@ bool ForceRenderRed( for (auto& fact : initial_facts.fact()) { fact_manager.AddFact(fact, ir_context.get()); } + TransformationContext transformation_context(&fact_manager, + validator_options); auto entry_point_function = FindFragmentShaderEntryPoint(ir_context.get(), message_consumer); @@ -355,8 +359,9 @@ bool ForceRenderRed( for (auto& replacement : {first_greater_then_operand_replacement.get(), second_greater_then_operand_replacement.get()}) { if (replacement) { - assert(replacement->IsApplicable(ir_context.get(), fact_manager)); - replacement->Apply(ir_context.get(), &fact_manager); + assert(replacement->IsApplicable(ir_context.get(), + transformation_context)); + replacement->Apply(ir_context.get(), &transformation_context); } } } diff --git a/source/fuzz/force_render_red.h b/source/fuzz/force_render_red.h index 2484d27883..b51c72b464 100644 --- a/source/fuzz/force_render_red.h +++ b/source/fuzz/force_render_red.h @@ -38,7 +38,8 @@ namespace fuzz { // instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for // which it is known that 'u < v' holds. bool ForceRenderRed( - const spv_target_env& target_env, const std::vector& binary_in, + const spv_target_env& target_env, spv_validator_options validator_options, + const std::vector& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, std::vector* binary_out); diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 3a4fa0eca7..6524c217d2 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -51,6 +51,7 @@ #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/build_module.h" #include "source/spirv_fuzzer_options.h" #include "source/util/make_unique.h" @@ -66,19 +67,19 @@ const uint32_t kTransformationLimit = 500; const uint32_t kChanceOfApplyingAnotherPass = 85; // A convenience method to add a fuzzer pass to |passes| with probability 0.5. -// All fuzzer passes take |ir_context|, |fact_manager|, |fuzzer_context| and -// |transformation_sequence_out| as parameters. Extra arguments can be provided -// via |extra_args|. +// All fuzzer passes take |ir_context|, |transformation_context|, +// |fuzzer_context| and |transformation_sequence_out| as parameters. Extra +// arguments can be provided via |extra_args|. template void MaybeAddPass( std::vector>* passes, - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformation_sequence_out, Args&&... extra_args) { if (fuzzer_context->ChooseEven()) { - passes->push_back(MakeUnique(ir_context, fact_manager, fuzzer_context, - transformation_sequence_out, + passes->push_back(MakeUnique(ir_context, transformation_context, + fuzzer_context, transformation_sequence_out, std::forward(extra_args)...)); } } @@ -182,11 +183,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( FactManager fact_manager; fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get()); + TransformationContext transformation_context(&fact_manager, + impl_->validator_options); // Add some essential ingredients to the module if they are not already // present, such as boolean constants. FuzzerPassAddUsefulConstructs add_useful_constructs( - ir_context.get(), &fact_manager, &fuzzer_context, + ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context, tools)) { @@ -196,69 +199,69 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( // Apply some semantics-preserving passes. std::vector> passes; while (passes.empty()) { - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( - &passes, ir_context.get(), &fact_manager, &fuzzer_context, + &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), &fact_manager, - &fuzzer_context, + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), + &transformation_context, &fuzzer_context, transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), &fact_manager, - &fuzzer_context, + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), + &transformation_context, &fuzzer_context, transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( - &passes, ir_context.get(), &fact_manager, &fuzzer_context, + &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out, donor_suppliers); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( - &passes, ir_context.get(), &fact_manager, &fuzzer_context, + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); - MaybeAddPass(&passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); } bool is_first = true; @@ -279,25 +282,25 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( // as they do not unlock other passes. std::vector> final_passes; MaybeAddPass( - &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); - MaybeAddPass(&final_passes, ir_context.get(), - &fact_manager, &fuzzer_context, - transformation_sequence_out); MaybeAddPass( - &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); MaybeAddPass( - &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); MaybeAddPass( - &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); MaybeAddPass( - &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); MaybeAddPass( - &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); for (auto& pass : final_passes) { if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) { diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index a76f10d3a4..4ab29466d5 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -31,11 +31,12 @@ namespace spvtools { namespace fuzz { -FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager, +FuzzerPass::FuzzerPass(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) : ir_context_(ir_context), - fact_manager_(fact_manager), + transformation_context_(transformation_context), fuzzer_context_(fuzzer_context), transformations_(transformations) {} diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 46ee4080af..436fd77852 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -18,9 +18,9 @@ #include #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -29,22 +29,25 @@ namespace fuzz { // Interface for applying a pass of transformations to a module. class FuzzerPass { public: - FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPass(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); virtual ~FuzzerPass(); // Applies the pass to the module |ir_context_|, assuming and updating - // facts from |fact_manager_|, and using |fuzzer_context_| to guide the - // process. Appends to |transformations_| all transformations that were - // applied during the pass. + // information from |transformation_context_|, and using |fuzzer_context_| to + // guide the process. Appends to |transformations_| all transformations that + // were applied during the pass. virtual void Apply() = 0; protected: opt::IRContext* GetIRContext() const { return ir_context_; } - FactManager* GetFactManager() const { return fact_manager_; } + TransformationContext* GetTransformationContext() const { + return transformation_context_; + } FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; } @@ -93,9 +96,10 @@ class FuzzerPass { // by construction, and adding it to the sequence of applied transformations. template void ApplyTransformation(const TransformationType& transformation) { - assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "Transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetFactManager()); + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } @@ -230,7 +234,7 @@ class FuzzerPass { const std::vector& constant_ids); opt::IRContext* ir_context_; - FactManager* fact_manager_; + TransformationContext* transformation_context_; FuzzerContext* fuzzer_context_; protobufs::TransformationSequence* transformations_; }; diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp index cfc2812b46..b9c1eed37b 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default; diff --git a/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h index 7e8ed6129f..8649296f05 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.h +++ b/source/fuzz/fuzzer_pass_add_access_chains.h @@ -26,7 +26,7 @@ namespace fuzz { class FuzzerPassAddAccessChains : public FuzzerPass { public: FuzzerPassAddAccessChains(opt::IRContext* ir_context, - FactManager* fact_manager, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp index 32c720e16f..9b0dda8823 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -22,10 +22,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default; diff --git a/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h index 29d4bb896f..87bc0ff3f5 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.h +++ b/source/fuzz/fuzzer_pass_add_composite_types.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassAddCompositeTypes : public FuzzerPass { public: FuzzerPassAddCompositeTypes( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index c9bc9c4859..4e9db1f36d 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default; @@ -53,8 +54,9 @@ void FuzzerPassAddDeadBlocks::Apply() { } // Apply all those transformations that are in fact applicable. for (auto& transformation : candidate_transformations) { - if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { - transformation.Apply(GetIRContext(), GetFactManager()); + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } } diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h index 01e3843db6..d78f08836c 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.h +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.h @@ -24,7 +24,8 @@ namespace fuzz { // passes can then manipulate such blocks. class FuzzerPassAddDeadBlocks : public FuzzerPass { public: - FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index aefc2fcd75..6b171dcd85 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default; @@ -79,8 +80,8 @@ void FuzzerPassAddDeadBreaks::Apply() { auto candidate_transformation = TransformationAddDeadBreak( block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(), std::move(phi_ids)); - if (candidate_transformation.IsApplicable(GetIRContext(), - *GetFactManager())) { + if (candidate_transformation.IsApplicable( + GetIRContext(), *GetTransformationContext())) { // Only consider a transformation as a candidate if it is applicable. candidate_transformations.push_back( std::move(candidate_transformation)); @@ -109,10 +110,11 @@ void FuzzerPassAddDeadBreaks::Apply() { candidate_transformations.erase(candidate_transformations.begin() + index); // Probabilistically decide whether to try to apply it vs. ignore it, in the // case that it is applicable. - if (transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingDeadBreak())) { - transformation.Apply(GetIRContext(), GetFactManager()); + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } } diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h index 12a5095f0e..c379eed51c 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.h +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.h @@ -23,7 +23,8 @@ namespace fuzz { // A fuzzer pass for adding dead break edges to the module. class FuzzerPassAddDeadBreaks : public FuzzerPass { public: - FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp index 852df3de6c..b8f07fbe42 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddDeadContinues::~FuzzerPassAddDeadContinues() = default; @@ -75,10 +76,11 @@ void FuzzerPassAddDeadContinues::Apply() { // Probabilistically decide whether to apply the transformation in the // case that it is applicable. if (candidate_transformation.IsApplicable(GetIRContext(), - *GetFactManager()) && + *GetTransformationContext()) && GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingDeadContinue())) { - candidate_transformation.Apply(GetIRContext(), GetFactManager()); + candidate_transformation.Apply(GetIRContext(), + GetTransformationContext()); *GetTransformations()->add_transformation() = candidate_transformation.ToMessage(); } diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h index d067f1c4d8..b2acb935e7 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.h +++ b/source/fuzz/fuzzer_pass_add_dead_continues.h @@ -24,7 +24,7 @@ namespace fuzz { class FuzzerPassAddDeadContinues : public FuzzerPass { public: FuzzerPassAddDeadContinues( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 7f34344cc3..49c4a8aae3 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -23,10 +23,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() = default; diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h index 84229c05c3..6e6497738a 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.h +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -27,7 +27,7 @@ namespace fuzz { class FuzzerPassAddEquationInstructions : public FuzzerPass { public: FuzzerPassAddEquationInstructions( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp index 545aa169ff..f666eb25ad 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -24,10 +24,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default; @@ -74,8 +75,9 @@ void FuzzerPassAddFunctionCalls::Apply() { while (!candidate_functions.empty()) { opt::Function* candidate_function = GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions); - if (!GetFactManager()->BlockIsDead(block->id()) && - !GetFactManager()->FunctionIsLivesafe( + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id()) && + !GetTransformationContext()->GetFactManager()->FunctionIsLivesafe( candidate_function->result_id())) { // Unless in a dead block, only livesafe functions can be invoked continue; @@ -132,9 +134,11 @@ FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters( default: return false; } - if (!GetFactManager()->BlockIsDead(block->id()) && - !GetFactManager()->PointeeValueIsIrrelevant( - inst->result_id())) { + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id()) && + !GetTransformationContext() + ->GetFactManager() + ->PointeeValueIsIrrelevant(inst->result_id())) { // We can only pass a pointer as an actual parameter // if the pointee value for the pointer is irrelevant, // or if the block from which we would make the diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h index 5d184fd168..8f75e8cf93 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.h +++ b/source/fuzz/fuzzer_pass_add_function_calls.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassAddFunctionCalls : public FuzzerPass { public: FuzzerPassAddFunctionCalls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index 1371f46c62..ce2b8eb1bb 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default; diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h index c71d147748..a907d360c7 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.h +++ b/source/fuzz/fuzzer_pass_add_global_variables.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassAddGlobalVariables : public FuzzerPass { public: FuzzerPassAddGlobalVariables( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index 851787fedf..16d88e3b43 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddLoads::FuzzerPassAddLoads( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddLoads::~FuzzerPassAddLoads() = default; diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h index 125bc5dbfd..c4d5b27e23 100644 --- a/source/fuzz/fuzzer_pass_add_loads.h +++ b/source/fuzz/fuzzer_pass_add_loads.h @@ -23,7 +23,8 @@ namespace fuzz { // Fuzzer pass that adds stores, at random, from pointers in the module. class FuzzerPassAddLoads : public FuzzerPass { public: - FuzzerPassAddLoads(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassAddLoads(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp index 8d6d80d632..ace9be266b 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -22,10 +22,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default; diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h index eed36657ff..08d26d8c38 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.h +++ b/source/fuzz/fuzzer_pass_add_local_variables.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassAddLocalVariables : public FuzzerPass { public: FuzzerPassAddLocalVariables( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp index 82fb539982..09627d0986 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -20,10 +20,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddNoContractionDecorations:: ~FuzzerPassAddNoContractionDecorations() = default; diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h index abe5bd7ad9..f32e5bc619 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -24,7 +24,7 @@ namespace fuzz { class FuzzerPassAddNoContractionDecorations : public FuzzerPass { public: FuzzerPassAddNoContractionDecorations( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp index 794ddc3def..e8871be805 100644 --- a/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddStores::FuzzerPassAddStores( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddStores::~FuzzerPassAddStores() = default; @@ -82,9 +83,13 @@ void FuzzerPassAddStores::Apply() { default: break; } - return GetFactManager()->BlockIsDead(block->id()) || - GetFactManager()->PointeeValueIsIrrelevant( - instruction->result_id()); + return GetTransformationContext() + ->GetFactManager() + ->BlockIsDead(block->id()) || + GetTransformationContext() + ->GetFactManager() + ->PointeeValueIsIrrelevant( + instruction->result_id()); }); // At this point, |relevant_pointers| contains all the pointers we might diff --git a/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h index 9daa9e0f2e..55ec67f2bf 100644 --- a/source/fuzz/fuzzer_pass_add_stores.h +++ b/source/fuzz/fuzzer_pass_add_stores.h @@ -25,7 +25,8 @@ namespace fuzz { // are known not to affect the module's overall behaviour. class FuzzerPassAddStores : public FuzzerPass { public: - FuzzerPassAddStores(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassAddStores(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp index 8552dfd299..a267612bb1 100644 --- a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp +++ b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp @@ -25,10 +25,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAddUsefulConstructs::~FuzzerPassAddUsefulConstructs() = default; @@ -49,9 +50,10 @@ void FuzzerPassAddUsefulConstructs::MaybeAddIntConstant( TransformationAddConstantScalar add_constant_int = TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(), int_type_id, data); - assert(add_constant_int.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(add_constant_int.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "Should be applicable by construction."); - add_constant_int.Apply(GetIRContext(), GetFactManager()); + add_constant_int.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = add_constant_int.ToMessage(); } } @@ -75,9 +77,10 @@ void FuzzerPassAddUsefulConstructs::MaybeAddFloatConstant( TransformationAddConstantScalar add_constant_float = TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(), float_type_id, data); - assert(add_constant_float.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(add_constant_float.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "Should be applicable by construction."); - add_constant_float.Apply(GetIRContext(), GetFactManager()); + add_constant_float.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = add_constant_float.ToMessage(); } @@ -90,9 +93,10 @@ void FuzzerPassAddUsefulConstructs::Apply() { if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) { auto add_type_boolean = TransformationAddTypeBoolean(GetFuzzerContext()->GetFreshId()); - assert(add_type_boolean.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(add_type_boolean.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "Should be applicable by construction."); - add_type_boolean.Apply(GetIRContext(), GetFactManager()); + add_type_boolean.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = add_type_boolean.ToMessage(); } @@ -105,9 +109,10 @@ void FuzzerPassAddUsefulConstructs::Apply() { if (!GetIRContext()->get_type_mgr()->GetId(&temp_int_type)) { TransformationAddTypeInt add_type_int = TransformationAddTypeInt( GetFuzzerContext()->GetFreshId(), 32, is_signed); - assert(add_type_int.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(add_type_int.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "Should be applicable by construction."); - add_type_int.Apply(GetIRContext(), GetFactManager()); + add_type_int.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = add_type_int.ToMessage(); } } @@ -119,9 +124,10 @@ void FuzzerPassAddUsefulConstructs::Apply() { if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) { TransformationAddTypeFloat add_type_float = TransformationAddTypeFloat(GetFuzzerContext()->GetFreshId(), 32); - assert(add_type_float.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(add_type_float.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "Should be applicable by construction."); - add_type_float.Apply(GetIRContext(), GetFactManager()); + add_type_float.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = add_type_float.ToMessage(); } } @@ -139,9 +145,9 @@ void FuzzerPassAddUsefulConstructs::Apply() { TransformationAddConstantBoolean add_constant_boolean( GetFuzzerContext()->GetFreshId(), boolean_value); assert(add_constant_boolean.IsApplicable(GetIRContext(), - *GetFactManager()) && + *GetTransformationContext()) && "Should be applicable by construction."); - add_constant_boolean.Apply(GetIRContext(), GetFactManager()); + add_constant_boolean.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = add_constant_boolean.ToMessage(); } @@ -168,8 +174,9 @@ void FuzzerPassAddUsefulConstructs::Apply() { // of the element // - a signed integer constant for each index required to access the element // - a constant for the constant value itself - for (auto& fact_and_type_id : - GetFactManager()->GetConstantUniformFactsAndTypes()) { + for (auto& fact_and_type_id : GetTransformationContext() + ->GetFactManager() + ->GetConstantUniformFactsAndTypes()) { uint32_t element_type_id = fact_and_type_id.second; assert(element_type_id); auto element_type = @@ -183,9 +190,10 @@ void FuzzerPassAddUsefulConstructs::Apply() { auto add_pointer = TransformationAddTypePointer(GetFuzzerContext()->GetFreshId(), SpvStorageClassUniform, element_type_id); - assert(add_pointer.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(add_pointer.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "Should be applicable by construction."); - add_pointer.Apply(GetIRContext(), GetFactManager()); + add_pointer.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = add_pointer.ToMessage(); } std::vector words; diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.h b/source/fuzz/fuzzer_pass_add_useful_constructs.h index 7dc00f13ef..17e87a82e8 100644 --- a/source/fuzz/fuzzer_pass_add_useful_constructs.h +++ b/source/fuzz/fuzzer_pass_add_useful_constructs.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassAddUsefulConstructs : public FuzzerPass { public: FuzzerPassAddUsefulConstructs( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp index fe229bca49..aa62d2f895 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -20,10 +20,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default; diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h index 02d36004ee..e20541b19e 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -24,7 +24,7 @@ namespace fuzz { class FuzzerPassAdjustFunctionControls : public FuzzerPass { public: FuzzerPassAdjustFunctionControls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp index c9843d0a25..f7addffe1c 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -20,10 +20,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default; diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h index e94560673e..ee5cd48302 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -24,7 +24,7 @@ namespace fuzz { class FuzzerPassAdjustLoopControls : public FuzzerPass { public: FuzzerPassAdjustLoopControls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp index 2d3d676535..32f5ea5fb2 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() = default; diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h index c3d71185ea..699dcb5a1f 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass { public: FuzzerPassAdjustMemoryOperandsMasks( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp index 397dfedb07..83b1854e16 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -20,10 +20,11 @@ namespace spvtools { namespace fuzz { FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() = default; diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h index b5b255ce3e..820b30d43c 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -24,7 +24,7 @@ namespace fuzz { class FuzzerPassAdjustSelectionControls : public FuzzerPass { public: FuzzerPassAdjustSelectionControls( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 5711f35885..30884a6029 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -25,16 +25,19 @@ namespace spvtools { namespace fuzz { FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default; void FuzzerPassApplyIdSynonyms::Apply() { for (auto id_with_known_synonyms : - GetFactManager()->GetIdsForWhichSynonymsAreKnown(GetIRContext())) { + GetTransformationContext() + ->GetFactManager() + ->GetIdsForWhichSynonymsAreKnown(GetIRContext())) { // Gather up all uses of |id_with_known_synonym|, and then subsequently // iterate over these uses. We use this separation because, when // considering a given use, we might apply a transformation that will @@ -70,7 +73,8 @@ void FuzzerPassApplyIdSynonyms::Apply() { } std::vector synonyms_to_try; - for (auto& data_descriptor : GetFactManager()->GetSynonymsForId( + for (auto& data_descriptor : + GetTransformationContext()->GetFactManager()->GetSynonymsForId( id_with_known_synonyms, GetIRContext())) { protobufs::DataDescriptor descriptor_for_this_id = MakeDataDescriptor(id_with_known_synonyms, {}); diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h index 1a0748eb4f..1a9213db1a 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -27,7 +27,7 @@ namespace fuzz { class FuzzerPassApplyIdSynonyms : public FuzzerPass { public: FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context, - FactManager* fact_manager, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index 330b9cfcfc..b1b08230bf 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -25,10 +25,11 @@ namespace spvtools { namespace fuzz { FuzzerPassConstructComposites::FuzzerPassConstructComposites( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default; @@ -143,9 +144,10 @@ void FuzzerPassConstructComposites::Apply() { TransformationCompositeConstruct transformation( chosen_composite_type, *constructor_arguments, instruction_descriptor, GetFuzzerContext()->GetFreshId()); - assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && "This transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetFactManager()); + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); }); diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h index 99ef31ff96..d293514d3b 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.h +++ b/source/fuzz/fuzzer_pass_construct_composites.h @@ -27,7 +27,7 @@ namespace fuzz { class FuzzerPassConstructComposites : public FuzzerPass { public: FuzzerPassConstructComposites( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp index 588cfb600f..f055b5987e 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -21,10 +21,11 @@ namespace spvtools { namespace fuzz { FuzzerPassCopyObjects::FuzzerPassCopyObjects( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default; diff --git a/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h index 5419459e9f..8de382e7f9 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.h +++ b/source/fuzz/fuzzer_pass_copy_objects.h @@ -23,7 +23,8 @@ namespace fuzz { // A fuzzer pass for adding adding copies of objects to the module. class FuzzerPassCopyObjects : public FuzzerPass { public: - FuzzerPassCopyObjects(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassCopyObjects(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 27d8a6e5ea..63ce7a64f4 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -40,11 +40,12 @@ namespace spvtools { namespace fuzz { FuzzerPassDonateModules::FuzzerPassDonateModules( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, const std::vector& donor_suppliers) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations), + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations), donor_suppliers_(donor_suppliers) {} FuzzerPassDonateModules::~FuzzerPassDonateModules() = default; @@ -62,7 +63,9 @@ void FuzzerPassDonateModules::Apply() { std::unique_ptr donor_ir_context = donor_suppliers_.at( GetFuzzerContext()->RandomIndex(donor_suppliers_))(); assert(donor_ir_context != nullptr && "Supplying of donor failed"); - assert(fuzzerutil::IsValid(donor_ir_context.get()) && + assert(fuzzerutil::IsValid( + donor_ir_context.get(), + GetTransformationContext()->GetValidatorOptions()) && "The donor module must be valid"); // Donate the supplied module. // diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index ef529db707..9087daf232 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -28,7 +28,7 @@ namespace fuzz { class FuzzerPassDonateModules : public FuzzerPass { public: FuzzerPassDonateModules( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, const std::vector& donor_suppliers); diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp index ca1bfb33f5..49778aea57 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.cpp +++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -22,10 +22,11 @@ namespace spvtools { namespace fuzz { FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default; @@ -44,7 +45,8 @@ void FuzzerPassMergeBlocks::Apply() { // For other blocks, we add a transformation to merge the block into its // predecessor if that transformation would be applicable. TransformationMergeBlocks transformation(block.id()); - if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { potential_transformations.push_back(transformation); } } @@ -54,8 +56,9 @@ void FuzzerPassMergeBlocks::Apply() { uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations); auto transformation = potential_transformations.at(index); potential_transformations.erase(potential_transformations.begin() + index); - if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { - transformation.Apply(GetIRContext(), GetFactManager()); + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } } diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h index 457e591376..1a6c2c2717 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.h +++ b/source/fuzz/fuzzer_pass_merge_blocks.h @@ -23,7 +23,8 @@ namespace fuzz { // A fuzzer pass for merging blocks in the module. class FuzzerPassMergeBlocks : public FuzzerPass { public: - FuzzerPassMergeBlocks(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassMergeBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index 2caf0c6b65..4ced507fc2 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -25,10 +25,11 @@ namespace spvtools { namespace fuzz { FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default; @@ -83,12 +84,13 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( bool_constant_use, lhs_id, rhs_id, comparison_opcode, GetFuzzerContext()->GetFreshId()); // The transformation should be applicable by construction. - assert(transformation.IsApplicable(GetIRContext(), *GetFactManager())); + assert( + transformation.IsApplicable(GetIRContext(), *GetTransformationContext())); // Applying this transformation yields a pointer to the new instruction that // computes the result of the binary expression. - auto binary_operator_instruction = - transformation.ApplyWithResult(GetIRContext(), GetFactManager()); + auto binary_operator_instruction = transformation.ApplyWithResult( + GetIRContext(), GetTransformationContext()); // Add this transformation to the sequence of transformations that have been // applied. @@ -245,7 +247,9 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant( // with uniforms of the same value. auto available_types_with_uniforms = - GetFactManager()->GetTypesForWhichUniformValuesAreKnown(); + GetTransformationContext() + ->GetFactManager() + ->GetTypesForWhichUniformValuesAreKnown(); if (available_types_with_uniforms.empty()) { // Do not try to obfuscate if we do not have access to any uniform // elements with known values. @@ -254,9 +258,10 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant( auto chosen_type_id = available_types_with_uniforms[GetFuzzerContext()->RandomIndex( available_types_with_uniforms)]; - auto available_constants = - GetFactManager()->GetConstantsAvailableFromUniformsForType( - GetIRContext(), chosen_type_id); + auto available_constants = GetTransformationContext() + ->GetFactManager() + ->GetConstantsAvailableFromUniformsForType( + GetIRContext(), chosen_type_id); if (available_constants.size() == 1) { // TODO(afd): for now we only obfuscate a boolean if there are at least // two constants available from uniforms, so that we can do a @@ -308,8 +313,11 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant( // Check whether we know that any uniforms are guaranteed to be equal to the // scalar constant associated with |constant_use|. - auto uniform_descriptors = GetFactManager()->GetUniformDescriptorsForConstant( - GetIRContext(), constant_use.id_of_interest()); + auto uniform_descriptors = + GetTransformationContext() + ->GetFactManager() + ->GetUniformDescriptorsForConstant(GetIRContext(), + constant_use.id_of_interest()); if (uniform_descriptors.empty()) { // No relevant uniforms, so do not obfuscate. return; @@ -324,8 +332,9 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant( constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId()); // Transformation should be applicable by construction. - assert(transformation.IsApplicable(GetIRContext(), *GetFactManager())); - transformation.Apply(GetIRContext(), GetFactManager()); + assert( + transformation.IsApplicable(GetIRContext(), *GetTransformationContext())); + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h index f34717b2e3..7755d211a9 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.h +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -28,7 +28,7 @@ namespace fuzz { class FuzzerPassObfuscateConstants : public FuzzerPass { public: FuzzerPassObfuscateConstants( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp index d59c195d7c..1665d050a6 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.cpp +++ b/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -23,10 +23,11 @@ namespace spvtools { namespace fuzz { FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default; @@ -88,8 +89,9 @@ void FuzzerPassOutlineFunctions::Apply() { /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(), /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id), /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id)); - if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { - transformation.Apply(GetIRContext(), GetFactManager()); + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } } diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h index 5448e7df75..6532ed9a32 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.h +++ b/source/fuzz/fuzzer_pass_outline_functions.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassOutlineFunctions : public FuzzerPass { public: FuzzerPassOutlineFunctions( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp index af6d2a5dbf..27a2d6710b 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.cpp +++ b/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -20,10 +20,11 @@ namespace spvtools { namespace fuzz { FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassPermuteBlocks::~FuzzerPassPermuteBlocks() = default; @@ -66,8 +67,9 @@ void FuzzerPassPermuteBlocks::Apply() { // down indefinitely. while (true) { TransformationMoveBlockDown transformation(*id); - if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { - transformation.Apply(GetIRContext(), GetFactManager()); + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } else { diff --git a/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h index 6735e952bf..f2d3b398b0 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.h +++ b/source/fuzz/fuzzer_pass_permute_blocks.h @@ -24,7 +24,8 @@ namespace fuzz { // manner. class FuzzerPassPermuteBlocks : public FuzzerPass { public: - FuzzerPassPermuteBlocks(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassPermuteBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp index 2c49860efc..57d9cabbc2 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -25,10 +25,11 @@ namespace spvtools { namespace fuzz { FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() = default; diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h index bc79804943..3f328642a4 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.h +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -30,7 +30,7 @@ namespace fuzz { class FuzzerPassPermuteFunctionParameters : public FuzzerPass { public: FuzzerPassPermuteFunctionParameters( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp index 6a2ea4dcc8..15c67901ea 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.cpp +++ b/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -23,10 +23,11 @@ namespace spvtools { namespace fuzz { FuzzerPassSplitBlocks::FuzzerPassSplitBlocks( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassSplitBlocks::~FuzzerPassSplitBlocks() = default; @@ -95,8 +96,9 @@ void FuzzerPassSplitBlocks::Apply() { // If the position we have chosen turns out to be a valid place to split // the block, we apply the split. Otherwise the block just doesn't get // split. - if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { - transformation.Apply(GetIRContext(), GetFactManager()); + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); *GetTransformations()->add_transformation() = transformation.ToMessage(); } } diff --git a/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h index 6e56dde955..278ec6dc3d 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.h +++ b/source/fuzz/fuzzer_pass_split_blocks.h @@ -24,7 +24,8 @@ namespace fuzz { // can be very useful for giving other passes a chance to apply. class FuzzerPassSplitBlocks : public FuzzerPass { public: - FuzzerPassSplitBlocks(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerPassSplitBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp index 4df97c94e2..321e8efd63 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -22,10 +22,11 @@ namespace spvtools { namespace fuzz { FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default; diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h index b0206de760..74d937d8c6 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.h +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -26,7 +26,7 @@ namespace fuzz { class FuzzerPassSwapCommutableOperands : public FuzzerPass { public: FuzzerPassSwapCommutableOperands( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp index 9fb175ba2b..4f26cba362 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -22,10 +22,11 @@ namespace spvtools { namespace fuzz { FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) - : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} FuzzerPassToggleAccessChainInstruction:: ~FuzzerPassToggleAccessChainInstruction() = default; diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h index ec8c3f78f9..d77c7cbe7b 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -25,7 +25,7 @@ namespace fuzz { class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { public: FuzzerPassToggleAccessChainInstruction( - opt::IRContext* ir_context, FactManager* fact_manager, + opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 4bfa195064..90cf9fe5cc 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -329,11 +329,11 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction, return array_length_constant->GetU32(); } -bool IsValid(opt::IRContext* context) { +bool IsValid(opt::IRContext* context, spv_validator_options validator_options) { std::vector binary; context->module()->ToBinary(&binary, false); SpirvTools tools(context->grammar().target_env()); - return tools.Validate(binary); + return tools.Validate(binary.data(), binary.size(), validator_options); } std::unique_ptr CloneIRContext(opt::IRContext* context) { diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 7be0d59eb6..08edfc5869 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -132,8 +132,9 @@ uint32_t GetNumberOfStructMembers( uint32_t GetArraySize(const opt::Instruction& array_type_instruction, opt::IRContext* context); -// Returns true if and only if |context| is valid, according to the validator. -bool IsValid(opt::IRContext* context); +// Returns true if and only if |context| is valid, according to the validator +// instantiated with |validator_options|. +bool IsValid(opt::IRContext* context, spv_validator_options validator_options); // Returns a clone of |context|, by writing |context| to a binary and then // parsing it again. diff --git a/source/fuzz/replayer.cpp b/source/fuzz/replayer.cpp index 07dfe72848..6312cbafb9 100644 --- a/source/fuzz/replayer.cpp +++ b/source/fuzz/replayer.cpp @@ -26,6 +26,7 @@ #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_context.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" @@ -99,16 +100,19 @@ Replayer::ReplayerResultStatus Replayer::Run( FactManager fact_manager; fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get()); + TransformationContext transformation_context(&fact_manager, + impl_->validator_options); // Consider the transformation proto messages in turn. for (auto& message : transformation_sequence_in.transformation()) { auto transformation = Transformation::FromMessage(message); // Check whether the transformation can be applied. - if (transformation->IsApplicable(ir_context.get(), fact_manager)) { + if (transformation->IsApplicable(ir_context.get(), + transformation_context)) { // The transformation is applicable, so apply it, and copy it to the // sequence of transformations that were applied. - transformation->Apply(ir_context.get(), &fact_manager); + transformation->Apply(ir_context.get(), &transformation_context); *transformation_sequence_out->add_transformation() = message; if (impl_->validate_during_replay) { diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index f18c86bddf..6f008fcd04 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -195,9 +195,9 @@ std::unique_ptr Transformation::FromMessage( } bool Transformation::CheckIdIsFreshAndNotUsedByThisTransformation( - uint32_t id, opt::IRContext* context, + uint32_t id, opt::IRContext* ir_context, std::set* ids_used_by_this_transformation) { - if (!fuzzerutil::IsFreshId(context, id)) { + if (!fuzzerutil::IsFreshId(ir_context, id)) { return false; } if (ids_used_by_this_transformation->count(id) != 0) { diff --git a/source/fuzz/transformation.h b/source/fuzz/transformation.h index dbe803f35c..dbd0fe2831 100644 --- a/source/fuzz/transformation.h +++ b/source/fuzz/transformation.h @@ -17,8 +17,8 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -60,19 +60,22 @@ class Transformation { public: // A precondition that determines whether the transformation can be cleanly // applied in a semantics-preserving manner to the SPIR-V module given by - // |context|, in the presence of facts captured by |fact_manager|. + // |ir_context|, in the presence of facts and other contextual information + // captured by |transformation_context|. + // // Preconditions for individual transformations must be documented in the - // associated header file using precise English. The fact manager is used to - // provide access to facts about the module that are known to be true, on + // associated header file using precise English. The transformation context + // provides access to facts about the module that are known to be true, on // which the precondition may depend. - virtual bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const = 0; + virtual bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const = 0; - // Requires that IsApplicable(context, fact_manager) holds. Applies the - // transformation, mutating |context| and possibly updating |fact_manager| - // with new facts established by the transformation. - virtual void Apply(opt::IRContext* context, - FactManager* fact_manager) const = 0; + // Requires that IsApplicable(ir_context, *transformation_context) holds. + // Applies the transformation, mutating |ir_context| and possibly updating + // |transformation_context| with new facts established by the transformation. + virtual void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const = 0; // Turns the transformation into a protobuf message for serialization. virtual protobufs::Transformation ToMessage() const = 0; @@ -90,7 +93,7 @@ class Transformation { // checking id freshness for a transformation that uses many ids, all of which // must be distinct. static bool CheckIdIsFreshAndNotUsedByThisTransformation( - uint32_t id, opt::IRContext* context, + uint32_t id, opt::IRContext* ir_context, std::set* ids_used_by_this_transformation); }; diff --git a/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp index 8c3100640e..ff17c3685e 100644 --- a/source/fuzz/transformation_access_chain.cpp +++ b/source/fuzz/transformation_access_chain.cpp @@ -40,19 +40,18 @@ TransformationAccessChain::TransformationAccessChain( } bool TransformationAccessChain::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The result id must be fresh - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The pointer id must exist and have a type. - auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id()); + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); if (!pointer || !pointer->type_id()) { return false; } // The type must indeed be a pointer - auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id()); + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); if (pointer_type->opcode() != SpvOpTypePointer) { return false; } @@ -60,7 +59,7 @@ bool TransformationAccessChain::IsApplicable( // The described instruction to insert before must exist and be a suitable // point where an OpAccessChain instruction could be inserted. auto instruction_to_insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); if (!instruction_to_insert_before) { return false; } @@ -86,7 +85,7 @@ bool TransformationAccessChain::IsApplicable( // The pointer on which the access chain is to be based needs to be available // (according to dominance rules) at the insertion point. if (!fuzzerutil::IdIsAvailableBeforeInstruction( - context, instruction_to_insert_before, message_.pointer_id())) { + ir_context, instruction_to_insert_before, message_.pointer_id())) { return false; } @@ -104,7 +103,7 @@ bool TransformationAccessChain::IsApplicable( // integer. Otherwise, the integer with which the id is associated is the // second component. std::pair maybe_index_value = - GetIndexValue(context, index_id); + GetIndexValue(ir_context, index_id); if (!maybe_index_value.first) { // There was no integer: this index is no good. return false; @@ -113,7 +112,7 @@ bool TransformationAccessChain::IsApplicable( // type is not a composite or the index is out of bounds, and the id of // the next type otherwise. subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( - context, subobject_type_id, maybe_index_value.second); + ir_context, subobject_type_id, maybe_index_value.second); if (!subobject_type_id) { // Either the type was not a composite (so that too many indices were // provided), or the index was out of bounds. @@ -128,13 +127,14 @@ bool TransformationAccessChain::IsApplicable( // We do not use the type manager to look up this type, due to problems // associated with pointers to isomorphic structs being regarded as the same. return fuzzerutil::MaybeGetPointerType( - context, subobject_type_id, + ir_context, subobject_type_id, static_cast( pointer_type->GetSingleWordInOperand(0))) != 0; } void TransformationAccessChain::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // The operands to the access chain are the pointer followed by the indices. // The result type of the access chain is determined by where the indices // lead. We thus push the pointer to a sequence of operands, and then follow @@ -148,8 +148,8 @@ void TransformationAccessChain::Apply( operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}); // Start walking the indices, starting with the pointer's base type. - auto pointer_type = context->get_def_use_mgr()->GetDef( - context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id()); + auto pointer_type = ir_context->get_def_use_mgr()->GetDef( + ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id()); uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); // Go through the index ids in turn. @@ -157,33 +157,35 @@ void TransformationAccessChain::Apply( // Add the index id to the operands. operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}}); // Get the integer value associated with the index id. - uint32_t index_value = GetIndexValue(context, index_id).second; + uint32_t index_value = GetIndexValue(ir_context, index_id).second; // Walk to the next type in the composite object using this index. subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( - context, subobject_type_id, index_value); + ir_context, subobject_type_id, index_value); } // The access chain's result type is a pointer to the composite component that // was reached after following all indices. The storage class is that of the // original pointer. uint32_t result_type = fuzzerutil::MaybeGetPointerType( - context, subobject_type_id, + ir_context, subobject_type_id, static_cast(pointer_type->GetSingleWordInOperand(0))); // Add the access chain instruction to the module, and update the module's id // bound. - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - FindInstruction(message_.instruction_to_insert_before(), context) - ->InsertBefore( - MakeUnique(context, SpvOpAccessChain, result_type, - message_.fresh_id(), operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpAccessChain, result_type, message_.fresh_id(), + operands)); // Conservatively invalidate all analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); // If the base pointer's pointee value was irrelevant, the same is true of the // pointee value of the result of this access chain. - if (fact_manager->PointeeValueIsIrrelevant(message_.pointer_id())) { - fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + message_.pointer_id())) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); } } @@ -194,8 +196,8 @@ protobufs::Transformation TransformationAccessChain::ToMessage() const { } std::pair TransformationAccessChain::GetIndexValue( - opt::IRContext* context, uint32_t index_id) const { - auto index_instruction = context->get_def_use_mgr()->GetDef(index_id); + opt::IRContext* ir_context, uint32_t index_id) const { + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) { // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could // allow non-constant indices when looking up non-structs, using clamping @@ -203,7 +205,7 @@ std::pair TransformationAccessChain::GetIndexValue( return {false, 0}; } auto index_type = - context->get_def_use_mgr()->GetDef(index_instruction->type_id()); + ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id()); if (index_type->opcode() != SpvOpTypeInt || index_type->GetSingleWordInOperand(0) != 32) { return {false, 0}; diff --git a/source/fuzz/transformation_access_chain.h b/source/fuzz/transformation_access_chain.h index 92d9e6a6e6..9306a596e4 100644 --- a/source/fuzz/transformation_access_chain.h +++ b/source/fuzz/transformation_access_chain.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -47,8 +47,9 @@ class TransformationAccessChain : public Transformation { // - If type t is the final type reached by walking these indices, the module // must include an instruction "OpTypePointer SC %t" where SC is the storage // class associated with |message_.pointer_id| - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an instruction of the form: // |message_.fresh_id| = OpAccessChain %ptr |message_.index_id| @@ -57,10 +58,12 @@ class TransformationAccessChain : public Transformation { // the indices in |message_.index_id|, and with the same storage class as // |message_.pointer_id|. // - // If |fact_manager| reports that |message_.pointer_id| has an irrelevant - // pointee value, then the fact that |message_.fresh_id| (the result of the - // access chain) also has an irrelevant pointee value is also recorded. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + // If the fact manager in |transformation_context| reports that + // |message_.pointer_id| has an irrelevant pointee value, then the fact that + // |message_.fresh_id| (the result of the access chain) also has an irrelevant + // pointee value is also recorded. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -68,7 +71,7 @@ class TransformationAccessChain : public Transformation { // Returns {false, 0} if |index_id| does not correspond to a 32-bit integer // constant. Otherwise, returns {true, value}, where value is the value of // the 32-bit integer constant to which |index_id| corresponds. - std::pair GetIndexValue(opt::IRContext* context, + std::pair GetIndexValue(opt::IRContext* ir_context, uint32_t index_id) const; protobufs::TransformationAccessChain message_; diff --git a/source/fuzz/transformation_add_constant_boolean.cpp b/source/fuzz/transformation_add_constant_boolean.cpp index 21c8ed3fcf..1930f7ef6e 100644 --- a/source/fuzz/transformation_add_constant_boolean.cpp +++ b/source/fuzz/transformation_add_constant_boolean.cpp @@ -31,27 +31,28 @@ TransformationAddConstantBoolean::TransformationAddConstantBoolean( } bool TransformationAddConstantBoolean::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { opt::analysis::Bool bool_type; - if (!context->get_type_mgr()->GetId(&bool_type)) { + if (!ir_context->get_type_mgr()->GetId(&bool_type)) { // No OpTypeBool is present. return false; } - return fuzzerutil::IsFreshId(context, message_.fresh_id()); + return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); } -void TransformationAddConstantBoolean::Apply(opt::IRContext* context, - FactManager* /*unused*/) const { +void TransformationAddConstantBoolean::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::analysis::Bool bool_type; // Add the boolean constant to the module, ensuring the module's id bound is // high enough. - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - context->module()->AddGlobalValue( + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + ir_context->module()->AddGlobalValue( message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse, - message_.fresh_id(), context->get_type_mgr()->GetId(&bool_type)); + message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type)); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const { diff --git a/source/fuzz/transformation_add_constant_boolean.h b/source/fuzz/transformation_add_constant_boolean.h index 79df1cdaf7..5d876cf6d8 100644 --- a/source/fuzz/transformation_add_constant_boolean.h +++ b/source/fuzz/transformation_add_constant_boolean.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -32,12 +32,14 @@ class TransformationAddConstantBoolean : public Transformation { // - |message_.fresh_id| must not be used by the module. // - The module must already contain OpTypeBool. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // - Adds OpConstantTrue (OpConstantFalse) to the module with id // |message_.fresh_id| if |message_.is_true| holds (does not hold). - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp index 7ba1ea4d7f..ae34b26275 100644 --- a/source/fuzz/transformation_add_constant_composite.cpp +++ b/source/fuzz/transformation_add_constant_composite.cpp @@ -37,15 +37,14 @@ TransformationAddConstantComposite::TransformationAddConstantComposite( } bool TransformationAddConstantComposite::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // Check that the given id is fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // Check that the composite type id is an instruction id. auto composite_type_instruction = - context->get_def_use_mgr()->GetDef(message_.type_id()); + ir_context->get_def_use_mgr()->GetDef(message_.type_id()); if (!composite_type_instruction) { return false; } @@ -56,7 +55,7 @@ bool TransformationAddConstantComposite::IsApplicable( case SpvOpTypeArray: for (uint32_t index = 0; index < - fuzzerutil::GetArraySize(*composite_type_instruction, context); + fuzzerutil::GetArraySize(*composite_type_instruction, ir_context); index++) { constituent_type_ids.push_back( composite_type_instruction->GetSingleWordInOperand(0)); @@ -93,7 +92,7 @@ bool TransformationAddConstantComposite::IsApplicable( // corresponding constituent type. for (uint32_t index = 0; index < constituent_type_ids.size(); index++) { auto constituent_instruction = - context->get_def_use_mgr()->GetDef(message_.constituent_id(index)); + ir_context->get_def_use_mgr()->GetDef(message_.constituent_id(index)); if (!constituent_instruction) { return false; } @@ -105,18 +104,19 @@ bool TransformationAddConstantComposite::IsApplicable( } void TransformationAddConstantComposite::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList in_operands; for (auto constituent_id : message_.constituent_id()) { in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}}); } - context->module()->AddGlobalValue(MakeUnique( - context, SpvOpConstantComposite, message_.type_id(), message_.fresh_id(), - in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddGlobalValue(MakeUnique( + ir_context, SpvOpConstantComposite, message_.type_id(), + message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddConstantComposite::ToMessage() diff --git a/source/fuzz/transformation_add_constant_composite.h b/source/fuzz/transformation_add_constant_composite.h index 9a824a047a..4fec561fc7 100644 --- a/source/fuzz/transformation_add_constant_composite.h +++ b/source/fuzz/transformation_add_constant_composite.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -38,13 +38,15 @@ class TransformationAddConstantComposite : public Transformation { // - |message_.type_id| must be the id of a composite type // - |message_.constituent_id| must refer to ids that match the constituent // types of this composite type - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpConstantComposite instruction defining a constant of type // |message_.type_id|, using |message_.constituent_id| as constituents, with // result id |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_constant_scalar.cpp b/source/fuzz/transformation_add_constant_scalar.cpp index 36af5e01cc..e13d08f7ad 100644 --- a/source/fuzz/transformation_add_constant_scalar.cpp +++ b/source/fuzz/transformation_add_constant_scalar.cpp @@ -33,14 +33,13 @@ TransformationAddConstantScalar::TransformationAddConstantScalar( } bool TransformationAddConstantScalar::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The id needs to be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The type id for the scalar must exist and be a type. - auto type = context->get_type_mgr()->GetType(message_.type_id()); + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); if (!type) { return false; } @@ -61,20 +60,21 @@ bool TransformationAddConstantScalar::IsApplicable( } void TransformationAddConstantScalar::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList operand_list; for (auto word : message_.word()) { operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}}); } - context->module()->AddGlobalValue( - MakeUnique(context, SpvOpConstant, message_.type_id(), - message_.fresh_id(), operand_list)); + ir_context->module()->AddGlobalValue(MakeUnique( + ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(), + operand_list)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddConstantScalar::ToMessage() const { diff --git a/source/fuzz/transformation_add_constant_scalar.h b/source/fuzz/transformation_add_constant_scalar.h index 914cfe62af..e0ed39fac9 100644 --- a/source/fuzz/transformation_add_constant_scalar.h +++ b/source/fuzz/transformation_add_constant_scalar.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -37,11 +37,13 @@ class TransformationAddConstantScalar : public Transformation { // - |message_.type_id| must be the id of a floating-point or integer type // - The size of |message_.word| must be compatible with the width of this // type - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds a new OpConstant instruction with the given type and words. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp index b58f75e520..b246c3fc84 100644 --- a/source/fuzz/transformation_add_dead_block.cpp +++ b/source/fuzz/transformation_add_dead_block.cpp @@ -32,16 +32,15 @@ TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id, } bool TransformationAddDeadBlock::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The new block's id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // First, we check that a constant with the same value as // |message_.condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstantId(context, + if (!fuzzerutil::MaybeGetBoolConstantId(ir_context, message_.condition_value())) { // The required constant is not present, so the transformation cannot be // applied. @@ -50,7 +49,7 @@ bool TransformationAddDeadBlock::IsApplicable( // The existing block must indeed exist. auto existing_block = - fuzzerutil::MaybeFindBlock(context, message_.existing_block()); + fuzzerutil::MaybeFindBlock(ir_context, message_.existing_block()); if (!existing_block) { return false; } @@ -68,13 +67,13 @@ bool TransformationAddDeadBlock::IsApplicable( // Its successor must not be a merge block nor continue target. auto successor_block_id = existing_block->terminator()->GetSingleWordInOperand(0); - if (fuzzerutil::IsMergeOrContinue(context, successor_block_id)) { + if (fuzzerutil::IsMergeOrContinue(ir_context, successor_block_id)) { return false; } // The successor must not be a loop header (i.e., |message_.existing_block| // must not be a back-edge block. - if (context->cfg()->block(successor_block_id)->IsLoopHeader()) { + if (ir_context->cfg()->block(successor_block_id)->IsLoopHeader()) { return false; } @@ -82,34 +81,36 @@ bool TransformationAddDeadBlock::IsApplicable( } void TransformationAddDeadBlock::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // Update the module id bound so that it is at least the id of the new block. - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // Get the existing block and its successor. - auto existing_block = context->cfg()->block(message_.existing_block()); + auto existing_block = ir_context->cfg()->block(message_.existing_block()); auto successor_block_id = existing_block->terminator()->GetSingleWordInOperand(0); // Get the id of the boolean value that will be used as the branch condition. - auto bool_id = - fuzzerutil::MaybeGetBoolConstantId(context, message_.condition_value()); + auto bool_id = fuzzerutil::MaybeGetBoolConstantId(ir_context, + message_.condition_value()); // Make a new block that unconditionally branches to the original successor // block. auto enclosing_function = existing_block->GetParent(); - std::unique_ptr new_block = MakeUnique( - MakeUnique(context, SpvOpLabel, 0, message_.fresh_id(), - opt::Instruction::OperandList())); + std::unique_ptr new_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.fresh_id(), + opt::Instruction::OperandList())); new_block->AddInstruction(MakeUnique( - context, SpvOpBranch, 0, 0, + ir_context, SpvOpBranch, 0, 0, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {successor_block_id}}}))); // Turn the original block into a selection merge, with its original successor // as the merge block. existing_block->terminator()->InsertBefore(MakeUnique( - context, SpvOpSelectionMerge, 0, 0, + ir_context, SpvOpSelectionMerge, 0, 0, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {successor_block_id}}, {SPV_OPERAND_TYPE_SELECTION_CONTROL, @@ -135,7 +136,8 @@ void TransformationAddDeadBlock::Apply( existing_block); // Record the fact that the new block is dead. - fact_manager->AddFactBlockIsDead(message_.fresh_id()); + transformation_context->GetFactManager()->AddFactBlockIsDead( + message_.fresh_id()); // Fix up OpPhi instructions in the successor block, so that the values they // yield when control has transferred from the new block are the same as if @@ -143,7 +145,7 @@ void TransformationAddDeadBlock::Apply( // to be valid since |message_.existing_block| dominates the new block by // construction. Other transformations can change these phi operands to more // interesting values. - context->cfg() + ir_context->cfg() ->block(successor_block_id) ->ForEachPhiInst([this](opt::Instruction* phi_inst) { // Copy the operand that provides the phi value for the first of any @@ -156,7 +158,7 @@ void TransformationAddDeadBlock::Apply( // Do not rely on any existing analysis results since the control flow graph // of the module has changed. - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationAddDeadBlock::ToMessage() const { diff --git a/source/fuzz/transformation_add_dead_block.h b/source/fuzz/transformation_add_dead_block.h index 059daca9b1..7d0761647f 100644 --- a/source/fuzz/transformation_add_dead_block.h +++ b/source/fuzz/transformation_add_dead_block.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -41,15 +41,17 @@ class TransformationAddDeadBlock : public Transformation { // - |message_.existing_block| must not be a back-edge block, since in this // case the newly-added block would lead to another back-edge to the // associated loop header - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Changes the OpBranch from |message_.existing_block| to its successor 's' // to an OpBranchConditional to either 's' or a new block, // |message_.fresh_id|, which itself unconditionally branches to 's'. The // conditional branch uses |message.condition_value| as its condition, and is // arranged so that control will pass to 's' at runtime. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp index 43847fada5..db9de7d238 100644 --- a/source/fuzz/transformation_add_dead_break.cpp +++ b/source/fuzz/transformation_add_dead_break.cpp @@ -14,8 +14,8 @@ #include "source/fuzz/transformation_add_dead_break.h" -#include "source/fuzz/fact_manager.h" #include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/basic_block.h" #include "source/opt/ir_context.h" #include "source/opt/struct_cfg_analysis.h" @@ -39,7 +39,7 @@ TransformationAddDeadBreak::TransformationAddDeadBreak( } bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( - opt::IRContext* context, opt::BasicBlock* bb_from) const { + opt::IRContext* ir_context, opt::BasicBlock* bb_from) const { // Look at the structured control flow associated with |from_block| and // check whether it is contained in an appropriate construct with merge id // |to_block| such that a break from |from_block| to |to_block| is legal. @@ -70,7 +70,7 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( // structured control flow construct. auto containing_construct = - context->GetStructuredCFGAnalysis()->ContainingConstruct( + ir_context->GetStructuredCFGAnalysis()->ContainingConstruct( message_.from_block()); if (!containing_construct) { // |from_block| is not in a construct from which we can break. @@ -79,7 +79,7 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( // Consider case (2) if (message_.to_block() == - context->cfg()->block(containing_construct)->MergeBlockId()) { + ir_context->cfg()->block(containing_construct)->MergeBlockId()) { // This looks like an instance of case (2). // However, the structured CFG analysis regards the continue construct of a // loop as part of the loop, but it is not legal to jump from a loop's @@ -90,28 +90,29 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( // currently allow a dead break from a back edge block, but we could and // ultimately should. return !fuzzerutil::BlockIsInLoopContinueConstruct( - context, message_.from_block(), containing_construct); + ir_context, message_.from_block(), containing_construct); } // Case (3) holds if and only if |to_block| is the merge block for this // innermost loop that contains |from_block| auto containing_loop_header = - context->GetStructuredCFGAnalysis()->ContainingLoop( + ir_context->GetStructuredCFGAnalysis()->ContainingLoop( message_.from_block()); if (containing_loop_header && message_.to_block() == - context->cfg()->block(containing_loop_header)->MergeBlockId()) { + ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) { return !fuzzerutil::BlockIsInLoopContinueConstruct( - context, message_.from_block(), containing_loop_header); + ir_context, message_.from_block(), containing_loop_header); } return false; } bool TransformationAddDeadBreak::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // First, we check that a constant with the same value as // |message_.break_condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstantId(context, + if (!fuzzerutil::MaybeGetBoolConstantId(ir_context, message_.break_condition_value())) { // The required constant is not present, so the transformation cannot be // applied. @@ -121,17 +122,17 @@ bool TransformationAddDeadBreak::IsApplicable( // Check that |message_.from_block| and |message_.to_block| really are block // ids opt::BasicBlock* bb_from = - fuzzerutil::MaybeFindBlock(context, message_.from_block()); + fuzzerutil::MaybeFindBlock(ir_context, message_.from_block()); if (bb_from == nullptr) { return false; } opt::BasicBlock* bb_to = - fuzzerutil::MaybeFindBlock(context, message_.to_block()); + fuzzerutil::MaybeFindBlock(ir_context, message_.to_block()); if (bb_to == nullptr) { return false; } - if (!fuzzerutil::BlockIsReachableInItsFunction(context, bb_to)) { + if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) { // If the target of the break is unreachable, we conservatively do not // allow adding a dead break, to avoid the compilations that arise due to // the lack of sensible dominance information for unreachable blocks. @@ -157,14 +158,14 @@ bool TransformationAddDeadBreak::IsApplicable( "The id of the block we found should match the target id for the break."); // Check whether the data passed to extend OpPhi instructions is appropriate. - if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from, bb_to, + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, bb_to, message_.phi_id())) { return false; } // Check that adding the break would respect the rules of structured // control flow. - if (!AddingBreakRespectsStructuredControlFlow(context, bb_from)) { + if (!AddingBreakRespectsStructuredControlFlow(ir_context, bb_from)) { return false; } @@ -177,16 +178,18 @@ bool TransformationAddDeadBreak::IsApplicable( // being places on the validator. This should be revisited if we are sure // the validator is complete with respect to checking structured control flow // rules. - auto cloned_context = fuzzerutil::CloneIRContext(context); + auto cloned_context = fuzzerutil::CloneIRContext(ir_context); ApplyImpl(cloned_context.get()); - return fuzzerutil::IsValid(cloned_context.get()); + return fuzzerutil::IsValid(cloned_context.get(), + transformation_context.GetValidatorOptions()); } -void TransformationAddDeadBreak::Apply(opt::IRContext* context, - FactManager* /*unused*/) const { - ApplyImpl(context); +void TransformationAddDeadBreak::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ApplyImpl(ir_context); // Invalidate all analyses - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddDeadBreak::ToMessage() const { @@ -196,10 +199,10 @@ protobufs::Transformation TransformationAddDeadBreak::ToMessage() const { } void TransformationAddDeadBreak::ApplyImpl( - spvtools::opt::IRContext* context) const { + spvtools::opt::IRContext* ir_context) const { fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( - context, context->cfg()->block(message_.from_block()), - context->cfg()->block(message_.to_block()), + ir_context, ir_context->cfg()->block(message_.from_block()), + ir_context->cfg()->block(message_.to_block()), message_.break_condition_value(), message_.phi_id()); } diff --git a/source/fuzz/transformation_add_dead_break.h b/source/fuzz/transformation_add_dead_break.h index 81a2c991b5..0ea921041e 100644 --- a/source/fuzz/transformation_add_dead_break.h +++ b/source/fuzz/transformation_add_dead_break.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -50,21 +50,23 @@ class TransformationAddDeadBreak : public Transformation { // maintain validity of the module. // In particular, the new branch must not lead to violations of the rule // that a use must be dominated by its definition. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Replaces the terminator of a with a conditional branch to b or c. // The boolean constant associated with |message_.break_condition_value| is // used as the condition, and the order of b and c is arranged such that // control is guaranteed to jump to c. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; private: // Returns true if and only if adding an edge from |bb_from| to // |message_.to_block| respects structured control flow. - bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* context, + bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* ir_context, opt::BasicBlock* bb_from) const; // Used by 'Apply' to actually apply the transformation to the module of @@ -73,7 +75,7 @@ class TransformationAddDeadBreak : public Transformation { // module. This is only invoked by 'IsApplicable' after certain basic // applicability checks have been made, ensuring that the invocation of this // method is legal. - void ApplyImpl(opt::IRContext* context) const; + void ApplyImpl(opt::IRContext* ir_context) const; protobufs::TransformationAddDeadBreak message_; }; diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp index 3a4875e3dc..1fc6d67150 100644 --- a/source/fuzz/transformation_add_dead_continue.cpp +++ b/source/fuzz/transformation_add_dead_continue.cpp @@ -34,11 +34,12 @@ TransformationAddDeadContinue::TransformationAddDeadContinue( } bool TransformationAddDeadContinue::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // First, we check that a constant with the same value as // |message_.continue_condition_value| is present. if (!fuzzerutil::MaybeGetBoolConstantId( - context, message_.continue_condition_value())) { + ir_context, message_.continue_condition_value())) { // The required constant is not present, so the transformation cannot be // applied. return false; @@ -46,7 +47,7 @@ bool TransformationAddDeadContinue::IsApplicable( // Check that |message_.from_block| really is a block id. opt::BasicBlock* bb_from = - fuzzerutil::MaybeFindBlock(context, message_.from_block()); + fuzzerutil::MaybeFindBlock(ir_context, message_.from_block()); if (bb_from == nullptr) { return false; } @@ -68,31 +69,33 @@ bool TransformationAddDeadContinue::IsApplicable( // Because the structured CFG analysis does not regard a loop header as part // of the loop it heads, we check first whether bb_from is a loop header // before using the structured CFG analysis. - auto loop_header = bb_from->IsLoopHeader() - ? message_.from_block() - : context->GetStructuredCFGAnalysis()->ContainingLoop( - message_.from_block()); + auto loop_header = + bb_from->IsLoopHeader() + ? message_.from_block() + : ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + message_.from_block()); if (!loop_header) { return false; } - auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId(); + auto continue_block = + ir_context->cfg()->block(loop_header)->ContinueBlockId(); if (!fuzzerutil::BlockIsReachableInItsFunction( - context, context->cfg()->block(continue_block))) { + ir_context, ir_context->cfg()->block(continue_block))) { // If the loop's continue block is unreachable, we conservatively do not // allow adding a dead continue, to avoid the compilations that arise due to // the lack of sensible dominance information for unreachable blocks. return false; } - if (fuzzerutil::BlockIsInLoopContinueConstruct(context, message_.from_block(), - loop_header)) { + if (fuzzerutil::BlockIsInLoopContinueConstruct( + ir_context, message_.from_block(), loop_header)) { // We cannot jump to the continue target from the continue construct. return false; } - if (context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) { + if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) { // A branch straight to the continue target that is also a merge block might // break the property that a construct header must dominate its merge block // (if the merge block is reachable). @@ -100,8 +103,8 @@ bool TransformationAddDeadContinue::IsApplicable( } // Check whether the data passed to extend OpPhi instructions is appropriate. - if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from, - context->cfg()->block(continue_block), + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, + ir_context->cfg()->block(continue_block), message_.phi_id())) { return false; } @@ -115,16 +118,18 @@ bool TransformationAddDeadContinue::IsApplicable( // being placed on the validator. This should be revisited if we are sure // the validator is complete with respect to checking structured control flow // rules. - auto cloned_context = fuzzerutil::CloneIRContext(context); + auto cloned_context = fuzzerutil::CloneIRContext(ir_context); ApplyImpl(cloned_context.get()); - return fuzzerutil::IsValid(cloned_context.get()); + return fuzzerutil::IsValid(cloned_context.get(), + transformation_context.GetValidatorOptions()); } -void TransformationAddDeadContinue::Apply(opt::IRContext* context, - FactManager* /*unused*/) const { - ApplyImpl(context); +void TransformationAddDeadContinue::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ApplyImpl(ir_context); // Invalidate all analyses - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddDeadContinue::ToMessage() const { @@ -134,16 +139,16 @@ protobufs::Transformation TransformationAddDeadContinue::ToMessage() const { } void TransformationAddDeadContinue::ApplyImpl( - spvtools::opt::IRContext* context) const { - auto bb_from = context->cfg()->block(message_.from_block()); + spvtools::opt::IRContext* ir_context) const { + auto bb_from = ir_context->cfg()->block(message_.from_block()); auto continue_block = bb_from->IsLoopHeader() ? bb_from->ContinueBlockId() - : context->GetStructuredCFGAnalysis()->LoopContinueBlock( + : ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock( message_.from_block()); assert(continue_block && "message_.from_block must be in a loop."); fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( - context, bb_from, context->cfg()->block(continue_block), + ir_context, bb_from, ir_context->cfg()->block(continue_block), message_.continue_condition_value(), message_.phi_id()); } diff --git a/source/fuzz/transformation_add_dead_continue.h b/source/fuzz/transformation_add_dead_continue.h index 86b4c93bd0..1053c1655d 100644 --- a/source/fuzz/transformation_add_dead_continue.h +++ b/source/fuzz/transformation_add_dead_continue.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -52,14 +52,16 @@ class TransformationAddDeadContinue : public Transformation { // In particular, adding an edge from somewhere in the loop to the continue // target must not prevent uses of ids in the continue target from being // dominated by the definitions of those ids. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Replaces the terminator of a with a conditional branch to b or c. // The boolean constant associated with |message_.continue_condition_value| is // used as the condition, and the order of b and c is arranged such that // control is guaranteed to jump to c. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -70,7 +72,7 @@ class TransformationAddDeadContinue : public Transformation { // module. This is only invoked by 'IsApplicable' after certain basic // applicability checks have been made, ensuring that the invocation of this // method is legal. - void ApplyImpl(opt::IRContext* context) const; + void ApplyImpl(opt::IRContext* ir_context) const; protobufs::TransformationAddDeadContinue message_; }; diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp index 8f0d3c9207..45fe342d64 100644 --- a/source/fuzz/transformation_add_function.cpp +++ b/source/fuzz/transformation_add_function.cpp @@ -56,8 +56,8 @@ TransformationAddFunction::TransformationAddFunction( } bool TransformationAddFunction::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& fact_manager) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // This transformation may use a lot of ids, all of which need to be fresh // and distinct. This set tracks them. std::set ids_used_by_this_transformation; @@ -66,7 +66,7 @@ bool TransformationAddFunction::IsApplicable( for (auto& instruction : message_.instruction()) { if (instruction.result_id()) { if (!CheckIdIsFreshAndNotUsedByThisTransformation( - instruction.result_id(), context, + instruction.result_id(), ir_context, &ids_used_by_this_transformation)) { return false; } @@ -77,28 +77,28 @@ bool TransformationAddFunction::IsApplicable( // Ensure that all ids provided for making the function livesafe are fresh // and distinct. if (!CheckIdIsFreshAndNotUsedByThisTransformation( - message_.loop_limiter_variable_id(), context, + message_.loop_limiter_variable_id(), ir_context, &ids_used_by_this_transformation)) { return false; } for (auto& loop_limiter_info : message_.loop_limiter_info()) { if (!CheckIdIsFreshAndNotUsedByThisTransformation( - loop_limiter_info.load_id(), context, + loop_limiter_info.load_id(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - loop_limiter_info.increment_id(), context, + loop_limiter_info.increment_id(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - loop_limiter_info.compare_id(), context, + loop_limiter_info.compare_id(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - loop_limiter_info.logical_op_id(), context, + loop_limiter_info.logical_op_id(), ir_context, &ids_used_by_this_transformation)) { return false; } @@ -107,11 +107,11 @@ bool TransformationAddFunction::IsApplicable( message_.access_chain_clamping_info()) { for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) { if (!CheckIdIsFreshAndNotUsedByThisTransformation( - pair.first(), context, &ids_used_by_this_transformation)) { + pair.first(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - pair.second(), context, &ids_used_by_this_transformation)) { + pair.second(), ir_context, &ids_used_by_this_transformation)) { return false; } } @@ -123,8 +123,8 @@ bool TransformationAddFunction::IsApplicable( // is taken here. // We first clone the current module, so that we can try adding the new - // function without risking wrecking |context|. - auto cloned_module = fuzzerutil::CloneIRContext(context); + // function without risking wrecking |ir_context|. + auto cloned_module = fuzzerutil::CloneIRContext(ir_context); // We try to add a function to the cloned module, which may fail if // |message_.instruction| is not sufficiently well-formed. @@ -134,12 +134,14 @@ bool TransformationAddFunction::IsApplicable( // Check whether the cloned module is still valid after adding the function. // If it is not, the transformation is not applicable. - if (!fuzzerutil::IsValid(cloned_module.get())) { + if (!fuzzerutil::IsValid(cloned_module.get(), + transformation_context.GetValidatorOptions())) { return false; } if (message_.is_livesafe()) { - if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) { + if (!TryToMakeFunctionLivesafe(cloned_module.get(), + transformation_context)) { return false; } // After making the function livesafe, we check validity of the module @@ -148,7 +150,8 @@ bool TransformationAddFunction::IsApplicable( // has the potential to make the module invalid when it was otherwise valid. // It is simpler to rely on the validator to guard against this than to // consider all scenarios when making a function livesafe. - if (!fuzzerutil::IsValid(cloned_module.get())) { + if (!fuzzerutil::IsValid(cloned_module.get(), + transformation_context.GetValidatorOptions())) { return false; } } @@ -156,10 +159,11 @@ bool TransformationAddFunction::IsApplicable( } void TransformationAddFunction::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // Add the function to the module. As the transformation is applicable, this // should succeed. - bool success = TryToAddFunction(context); + bool success = TryToAddFunction(ir_context); assert(success && "The function should be successfully added."); (void)(success); // Keep release builds happy (otherwise they may complain // that |success| is not used). @@ -172,16 +176,16 @@ void TransformationAddFunction::Apply( for (auto& instruction : message_.instruction()) { switch (instruction.opcode()) { case SpvOpFunctionParameter: - if (context->get_def_use_mgr() + if (ir_context->get_def_use_mgr() ->GetDef(instruction.result_type_id()) ->opcode() == SpvOpTypePointer) { - fact_manager->AddFactValueOfPointeeIsIrrelevant( - instruction.result_id()); + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id()); } break; case SpvOpVariable: - fact_manager->AddFactValueOfPointeeIsIrrelevant( - instruction.result_id()); + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id()); break; default: break; @@ -190,7 +194,7 @@ void TransformationAddFunction::Apply( if (message_.is_livesafe()) { // Make the function livesafe, which also should succeed. - success = TryToMakeFunctionLivesafe(context, *fact_manager); + success = TryToMakeFunctionLivesafe(ir_context, *transformation_context); assert(success && "It should be possible to make the function livesafe."); (void)(success); // Keep release builds happy. @@ -198,17 +202,18 @@ void TransformationAddFunction::Apply( assert(message_.instruction(0).opcode() == SpvOpFunction && "The first instruction of an 'add function' transformation must be " "OpFunction."); - fact_manager->AddFactFunctionIsLivesafe( + transformation_context->GetFactManager()->AddFactFunctionIsLivesafe( message_.instruction(0).result_id()); } else { // Inform the fact manager that all blocks in the function are dead. for (auto& inst : message_.instruction()) { if (inst.opcode() == SpvOpLabel) { - fact_manager->AddFactBlockIsDead(inst.result_id()); + transformation_context->GetFactManager()->AddFactBlockIsDead( + inst.result_id()); } } } - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationAddFunction::ToMessage() const { @@ -218,9 +223,9 @@ protobufs::Transformation TransformationAddFunction::ToMessage() const { } bool TransformationAddFunction::TryToAddFunction( - opt::IRContext* context) const { + opt::IRContext* ir_context) const { // This function returns false if |message_.instruction| was not well-formed - // enough to actually create a function and add it to |context|. + // enough to actually create a function and add it to |ir_context|. // A function must have at least some instructions. if (message_.instruction().empty()) { @@ -235,7 +240,7 @@ bool TransformationAddFunction::TryToAddFunction( // Make a function, headed by the OpFunction instruction. std::unique_ptr new_function = MakeUnique( - InstructionFromMessage(context, function_begin)); + InstructionFromMessage(ir_context, function_begin)); // Keeps track of which instruction protobuf message we are currently // considering. @@ -249,7 +254,7 @@ bool TransformationAddFunction::TryToAddFunction( message_.instruction(instruction_index).opcode() == SpvOpFunctionParameter) { new_function->AddParameter(InstructionFromMessage( - context, message_.instruction(instruction_index))); + ir_context, message_.instruction(instruction_index))); instruction_index++; } @@ -270,7 +275,7 @@ bool TransformationAddFunction::TryToAddFunction( // as its parent. std::unique_ptr block = MakeUnique(InstructionFromMessage( - context, message_.instruction(instruction_index))); + ir_context, message_.instruction(instruction_index))); block->SetParent(new_function.get()); // Consider successive instructions until we hit another label or the end @@ -281,7 +286,7 @@ bool TransformationAddFunction::TryToAddFunction( SpvOpFunctionEnd && message_.instruction(instruction_index).opcode() != SpvOpLabel) { block->AddInstruction(InstructionFromMessage( - context, message_.instruction(instruction_index))); + ir_context, message_.instruction(instruction_index))); instruction_index++; } // Add the block to the new function. @@ -295,22 +300,23 @@ bool TransformationAddFunction::TryToAddFunction( } // Set the function's final instruction, add the function to the module and // report success. - new_function->SetFunctionEnd( - InstructionFromMessage(context, message_.instruction(instruction_index))); - context->AddFunction(std::move(new_function)); + new_function->SetFunctionEnd(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + ir_context->AddFunction(std::move(new_function)); - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); return true; } bool TransformationAddFunction::TryToMakeFunctionLivesafe( - opt::IRContext* context, const FactManager& fact_manager) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { assert(message_.is_livesafe() && "Precondition: is_livesafe must hold."); // Get a pointer to the added function. opt::Function* added_function = nullptr; - for (auto& function : *context->module()) { + for (auto& function : *ir_context->module()) { if (function.result_id() == message_.instruction(0).result_id()) { added_function = &function; break; @@ -318,7 +324,7 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe( } assert(added_function && "The added function should have been found."); - if (!TryToAddLoopLimiters(context, added_function)) { + if (!TryToAddLoopLimiters(ir_context, added_function)) { // Adding loop limiters did not work; bail out. return false; } @@ -332,20 +338,20 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe( switch (inst.opcode()) { case SpvOpKill: case SpvOpUnreachable: - if (!TryToTurnKillOrUnreachableIntoReturn(context, added_function, + if (!TryToTurnKillOrUnreachableIntoReturn(ir_context, added_function, &inst)) { return false; } break; case SpvOpAccessChain: case SpvOpInBoundsAccessChain: - if (!TryToClampAccessChainIndices(context, &inst)) { + if (!TryToClampAccessChainIndices(ir_context, &inst)) { return false; } break; case SpvOpFunctionCall: // A livesafe function my only call other livesafe functions. - if (!fact_manager.FunctionIsLivesafe( + if (!transformation_context.GetFactManager()->FunctionIsLivesafe( inst.GetSingleWordInOperand(0))) { return false; } @@ -358,7 +364,7 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe( } bool TransformationAddFunction::TryToAddLoopLimiters( - opt::IRContext* context, opt::Function* added_function) const { + opt::IRContext* ir_context, opt::Function* added_function) const { // Collect up all the loop headers so that we can subsequently add loop // limiting logic. std::vector loop_headers; @@ -377,7 +383,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // manipulating a loop limiter. auto loop_limit_constant_id_instr = - context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id()); + ir_context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id()); if (!loop_limit_constant_id_instr || loop_limit_constant_id_instr->opcode() != SpvOpConstant) { // The loop limit constant id instruction must exist and have an @@ -385,7 +391,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( return false; } - auto loop_limit_type = context->get_def_use_mgr()->GetDef( + auto loop_limit_type = ir_context->get_def_use_mgr()->GetDef( loop_limit_constant_id_instr->type_id()); if (loop_limit_type->opcode() != SpvOpTypeInt || loop_limit_type->GetSingleWordInOperand(0) != 32) { @@ -397,36 +403,36 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // Find the id of the "unsigned int" type. opt::analysis::Integer unsigned_int_type(32, false); uint32_t unsigned_int_type_id = - context->get_type_mgr()->GetId(&unsigned_int_type); + ir_context->get_type_mgr()->GetId(&unsigned_int_type); if (!unsigned_int_type_id) { // Unsigned int is not available; we need this type in order to add loop // limiters. return false; } auto registered_unsigned_int_type = - context->get_type_mgr()->GetRegisteredType(&unsigned_int_type); + ir_context->get_type_mgr()->GetRegisteredType(&unsigned_int_type); // Look for 0 of type unsigned int. opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(), {0}); - auto registered_zero = context->get_constant_mgr()->FindConstant(&zero); + auto registered_zero = ir_context->get_constant_mgr()->FindConstant(&zero); if (!registered_zero) { // We need 0 in order to be able to initialize loop limiters. return false; } - uint32_t zero_id = context->get_constant_mgr() + uint32_t zero_id = ir_context->get_constant_mgr() ->GetDefiningInstruction(registered_zero) ->result_id(); // Look for 1 of type unsigned int. opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(), {1}); - auto registered_one = context->get_constant_mgr()->FindConstant(&one); + auto registered_one = ir_context->get_constant_mgr()->FindConstant(&one); if (!registered_one) { // We need 1 in order to be able to increment loop limiters. return false; } - uint32_t one_id = context->get_constant_mgr() + uint32_t one_id = ir_context->get_constant_mgr() ->GetDefiningInstruction(registered_one) ->result_id(); @@ -434,7 +440,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( opt::analysis::Pointer pointer_to_unsigned_int_type( registered_unsigned_int_type, SpvStorageClassFunction); uint32_t pointer_to_unsigned_int_type_id = - context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type); + ir_context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type); if (!pointer_to_unsigned_int_type_id) { // We need pointer-to-unsigned int in order to declare the loop limiter // variable. @@ -443,7 +449,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // Look for bool type. opt::analysis::Bool bool_type; - uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type); + uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type); if (!bool_type_id) { // We need bool in order to compare the loop limiter's value with the loop // limit constant. @@ -454,22 +460,23 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // block, via an instruction of the form: // %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero added_function->begin()->begin()->InsertBefore(MakeUnique( - context, SpvOpVariable, pointer_to_unsigned_int_type_id, + ir_context, SpvOpVariable, pointer_to_unsigned_int_type_id, message_.loop_limiter_variable_id(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, {SPV_OPERAND_TYPE_ID, {zero_id}}}))); // Update the module's id bound since we have added the loop limiter // variable id. - fuzzerutil::UpdateModuleIdBound(context, message_.loop_limiter_variable_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.loop_limiter_variable_id()); // Consider each loop in turn. for (auto loop_header : loop_headers) { // Look for the loop's back-edge block. This is a predecessor of the loop // header that is dominated by the loop header. uint32_t back_edge_block_id = 0; - for (auto pred : context->cfg()->preds(loop_header->id())) { - if (context->GetDominatorAnalysis(added_function) + for (auto pred : ir_context->cfg()->preds(loop_header->id())) { + if (ir_context->GetDominatorAnalysis(added_function) ->Dominates(loop_header->id(), pred)) { back_edge_block_id = pred; break; @@ -481,7 +488,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // move on from this loop. continue; } - auto back_edge_block = context->cfg()->block(back_edge_block_id); + auto back_edge_block = ir_context->cfg()->block(back_edge_block_id); // Go through the sequence of loop limiter infos and find the one // corresponding to this loop. @@ -579,14 +586,15 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // Add a load from the loop limiter variable, of the form: // %t1 = OpLoad %uint32 %loop_limiter new_instructions.push_back(MakeUnique( - context, SpvOpLoad, unsigned_int_type_id, loop_limiter_info.load_id(), + ir_context, SpvOpLoad, unsigned_int_type_id, + loop_limiter_info.load_id(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}}))); // Increment the loaded value: // %t2 = OpIAdd %uint32 %t1 %one new_instructions.push_back(MakeUnique( - context, SpvOpIAdd, unsigned_int_type_id, + ir_context, SpvOpIAdd, unsigned_int_type_id, loop_limiter_info.increment_id(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}}, @@ -595,7 +603,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // Store the incremented value back to the loop limiter variable: // OpStore %loop_limiter %t2 new_instructions.push_back(MakeUnique( - context, SpvOpStore, 0, 0, + ir_context, SpvOpStore, 0, 0, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}, {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}}))); @@ -605,7 +613,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // or // %t3 = OpULessThan %bool %t1 %loop_limit new_instructions.push_back(MakeUnique( - context, + ir_context, compare_using_greater_than_equal ? SpvOpUGreaterThanEqual : SpvOpULessThan, bool_type_id, loop_limiter_info.compare_id(), @@ -615,7 +623,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters( if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) { new_instructions.push_back(MakeUnique( - context, + ir_context, compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd, bool_type_id, loop_limiter_info.logical_op_id(), opt::Instruction::OperandList( @@ -644,8 +652,9 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // Check that, if the merge block starts with OpPhi instructions, suitable // ids have been provided to give these instructions a value corresponding // to the new incoming edge from the back edge block. - auto merge_block = context->cfg()->block(loop_header->MergeBlockId()); - if (!fuzzerutil::PhiIdsOkForNewEdge(context, back_edge_block, merge_block, + auto merge_block = ir_context->cfg()->block(loop_header->MergeBlockId()); + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, back_edge_block, + merge_block, loop_limiter_info.phi_id())) { return false; } @@ -681,16 +690,18 @@ bool TransformationAddFunction::TryToAddLoopLimiters( // Update the module's id bound with respect to the various ids that // have been used for loop limiter manipulation. - fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.load_id()); - fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.increment_id()); - fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.compare_id()); - fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.logical_op_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.load_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + loop_limiter_info.increment_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.compare_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + loop_limiter_info.logical_op_id()); } return true; } bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn( - opt::IRContext* context, opt::Function* added_function, + opt::IRContext* ir_context, opt::Function* added_function, opt::Instruction* kill_or_unreachable_inst) const { assert((kill_or_unreachable_inst->opcode() == SpvOpKill || kill_or_unreachable_inst->opcode() == SpvOpUnreachable) && @@ -698,7 +709,7 @@ bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn( // Get the function's return type. auto function_return_type_inst = - context->get_def_use_mgr()->GetDef(added_function->type_id()); + ir_context->get_def_use_mgr()->GetDef(added_function->type_id()); if (function_return_type_inst->opcode() == SpvOpTypeVoid) { // The function has void return type, so change this instruction to @@ -712,7 +723,7 @@ bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn( // We first check that the id, %id, provided with the transformation // specifically to turn OpKill and OpUnreachable instructions into // OpReturnValue %id has the same type as the function's return type. - if (context->get_def_use_mgr() + if (ir_context->get_def_use_mgr() ->GetDef(message_.kill_unreachable_return_value_id()) ->type_id() != function_return_type_inst->result_id()) { return false; @@ -725,7 +736,7 @@ bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn( } bool TransformationAddFunction::TryToClampAccessChainIndices( - opt::IRContext* context, opt::Instruction* access_chain_inst) const { + opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const { assert((access_chain_inst->opcode() == SpvOpAccessChain || access_chain_inst->opcode() == SpvOpInBoundsAccessChain) && "Precondition: instruction must be OpAccessChain or " @@ -756,14 +767,14 @@ bool TransformationAddFunction::TryToClampAccessChainIndices( // Walk the access chain, clamping each index to be within bounds if it is // not a constant. - auto base_object = context->get_def_use_mgr()->GetDef( + auto base_object = ir_context->get_def_use_mgr()->GetDef( access_chain_inst->GetSingleWordInOperand(0)); assert(base_object && "The base object must exist."); auto pointer_type = - context->get_def_use_mgr()->GetDef(base_object->type_id()); + ir_context->get_def_use_mgr()->GetDef(base_object->type_id()); assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer && "The base object must have pointer type."); - auto should_be_composite_type = context->get_def_use_mgr()->GetDef( + auto should_be_composite_type = ir_context->get_def_use_mgr()->GetDef( pointer_type->GetSingleWordInOperand(1)); // Consider each index input operand in turn (operand 0 is the base object). @@ -784,18 +795,18 @@ bool TransformationAddFunction::TryToClampAccessChainIndices( // Get the bound for the composite being indexed into; e.g. the number of // columns of matrix or the size of an array. uint32_t bound = - GetBoundForCompositeIndex(context, *should_be_composite_type); + GetBoundForCompositeIndex(ir_context, *should_be_composite_type); // Get the instruction associated with the index and figure out its integer // type. const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index); - auto index_inst = context->get_def_use_mgr()->GetDef(index_id); + auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id); auto index_type_inst = - context->get_def_use_mgr()->GetDef(index_inst->type_id()); + ir_context->get_def_use_mgr()->GetDef(index_inst->type_id()); assert(index_type_inst->opcode() == SpvOpTypeInt); assert(index_type_inst->GetSingleWordInOperand(0) == 32); opt::analysis::Integer* index_int_type = - context->get_type_mgr() + ir_context->get_type_mgr() ->GetType(index_type_inst->result_id()) ->AsInteger(); @@ -805,20 +816,20 @@ bool TransformationAddFunction::TryToClampAccessChainIndices( "Access chain indices into structures are required to be " "constants."); opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1}); - if (!context->get_constant_mgr()->FindConstant(&bound_minus_one)) { + if (!ir_context->get_constant_mgr()->FindConstant(&bound_minus_one)) { // We do not have an integer constant whose value is |bound| -1. return false; } opt::analysis::Bool bool_type; - uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type); + uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type); if (!bool_type_id) { // Bool type is not declared; we cannot do a comparison. return false; } uint32_t bound_minus_one_id = - context->get_constant_mgr() + ir_context->get_constant_mgr() ->GetDefiningInstruction(&bound_minus_one) ->result_id(); @@ -832,7 +843,7 @@ bool TransformationAddFunction::TryToClampAccessChainIndices( // Compare the index with the bound via an instruction of the form: // %t1 = OpULessThanEqual %bool %index %bound_minus_one new_instructions.push_back(MakeUnique( - context, SpvOpULessThanEqual, bool_type_id, compare_id, + ir_context, SpvOpULessThanEqual, bool_type_id, compare_id, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); @@ -840,7 +851,7 @@ bool TransformationAddFunction::TryToClampAccessChainIndices( // Select the index if in-bounds, otherwise one less than the bound: // %t2 = OpSelect %int_type %t1 %index %bound_minus_one new_instructions.push_back(MakeUnique( - context, SpvOpSelect, index_type_inst->result_id(), select_id, + ir_context, SpvOpSelect, index_type_inst->result_id(), select_id, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {compare_id}}, {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, @@ -851,8 +862,8 @@ bool TransformationAddFunction::TryToClampAccessChainIndices( // Replace %index with %t2. access_chain_inst->SetInOperand(index, {select_id}); - fuzzerutil::UpdateModuleIdBound(context, compare_id); - fuzzerutil::UpdateModuleIdBound(context, select_id); + fuzzerutil::UpdateModuleIdBound(ir_context, compare_id); + fuzzerutil::UpdateModuleIdBound(ir_context, select_id); } else { // TODO(afd): At present the SPIR-V spec is not clear on whether // statically out-of-bounds indices mean that a module is invalid (so @@ -870,16 +881,16 @@ bool TransformationAddFunction::TryToClampAccessChainIndices( } } should_be_composite_type = - FollowCompositeIndex(context, *should_be_composite_type, index_id); + FollowCompositeIndex(ir_context, *should_be_composite_type, index_id); } return true; } uint32_t TransformationAddFunction::GetBoundForCompositeIndex( - opt::IRContext* context, const opt::Instruction& composite_type_inst) { + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst) { switch (composite_type_inst.opcode()) { case SpvOpTypeArray: - return fuzzerutil::GetArraySize(composite_type_inst, context); + return fuzzerutil::GetArraySize(composite_type_inst, ir_context); case SpvOpTypeMatrix: case SpvOpTypeVector: return composite_type_inst.GetSingleWordInOperand(1); @@ -893,7 +904,7 @@ uint32_t TransformationAddFunction::GetBoundForCompositeIndex( } opt::Instruction* TransformationAddFunction::FollowCompositeIndex( - opt::IRContext* context, const opt::Instruction& composite_type_inst, + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst, uint32_t index_id) { uint32_t sub_object_type_id; switch (composite_type_inst.opcode()) { @@ -905,12 +916,12 @@ opt::Instruction* TransformationAddFunction::FollowCompositeIndex( sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); break; case SpvOpTypeStruct: { - auto index_inst = context->get_def_use_mgr()->GetDef(index_id); + auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id); assert(index_inst->opcode() == SpvOpConstant); - assert( - context->get_def_use_mgr()->GetDef(index_inst->type_id())->opcode() == - SpvOpTypeInt); - assert(context->get_def_use_mgr() + assert(ir_context->get_def_use_mgr() + ->GetDef(index_inst->type_id()) + ->opcode() == SpvOpTypeInt); + assert(ir_context->get_def_use_mgr() ->GetDef(index_inst->type_id()) ->GetSingleWordInOperand(0) == 32); uint32_t index_value = index_inst->GetSingleWordInOperand(0); @@ -924,7 +935,7 @@ opt::Instruction* TransformationAddFunction::FollowCompositeIndex( break; } assert(sub_object_type_id && "No sub-object found."); - return context->get_def_use_mgr()->GetDef(sub_object_type_id); + return ir_context->get_def_use_mgr()->GetDef(sub_object_type_id); } } // namespace fuzz diff --git a/source/fuzz/transformation_add_function.h b/source/fuzz/transformation_add_function.h index 848b799fca..5af197b617 100644 --- a/source/fuzz/transformation_add_function.h +++ b/source/fuzz/transformation_add_function.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -47,12 +47,14 @@ class TransformationAddFunction : public Transformation { // ingredients to make the function livesafe, and the function must only // invoke other livesafe functions // - Adding the created function to the module must lead to a valid module. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds the function defined by |message_.instruction| to the module, making // it livesafe if |message_.is_livesafe| holds. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -61,26 +63,26 @@ class TransformationAddFunction : public Transformation { // an array, the number of components of a vector, or the number of columns of // a matrix. static uint32_t GetBoundForCompositeIndex( - opt::IRContext* context, const opt::Instruction& composite_type_inst); + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst); // Helper method that, given composite type |composite_type_inst|, returns the // type of the sub-object at index |index_id|, which is required to be in- // bounds. static opt::Instruction* FollowCompositeIndex( - opt::IRContext* context, const opt::Instruction& composite_type_inst, + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst, uint32_t index_id); private: // Attempts to create a function from the series of instructions in - // |message_.instruction| and add it to |context|. + // |message_.instruction| and add it to |ir_context|. // // Returns false if adding the function is not possible due to the messages // not respecting the basic structure of a function, e.g. if there is no - // OpFunction instruction or no blocks; in this case |context| is left in an - // indeterminate state. + // OpFunction instruction or no blocks; in this case |ir_context| is left in + // an indeterminate state. // - // Otherwise returns true. Whether |context| is valid after addition of the - // function depends on the contents of |message_.instruction|. + // Otherwise returns true. Whether |ir_context| is valid after addition of + // the function depends on the contents of |message_.instruction|. // // Intended usage: // - Perform a dry run of this method on a clone of a module, and use @@ -89,30 +91,31 @@ class TransformationAddFunction : public Transformation { // added, or leads to an invalid module. // - If the dry run succeeds, run the method on the real module of interest, // to add the function. - bool TryToAddFunction(opt::IRContext* context) const; + bool TryToAddFunction(opt::IRContext* ir_context) const; // Should only be called if |message_.is_livesafe| holds. Attempts to make // the function livesafe (see FactFunctionIsLivesafe for a definition). - // Returns false if this is not possible, due to |message_| or |context| not - // containing sufficient ingredients (such as types and fresh ids) to add + // Returns false if this is not possible, due to |message_| or |ir_context| + // not containing sufficient ingredients (such as types and fresh ids) to add // the instrumentation necessary to make the function livesafe. - bool TryToMakeFunctionLivesafe(opt::IRContext* context, - const FactManager& fact_manager) const; + bool TryToMakeFunctionLivesafe( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; // A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting // logic. - bool TryToAddLoopLimiters(opt::IRContext* context, + bool TryToAddLoopLimiters(opt::IRContext* ir_context, opt::Function* added_function) const; // A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and // OpUnreachable instructions into return instructions. bool TryToTurnKillOrUnreachableIntoReturn( - opt::IRContext* context, opt::Function* added_function, + opt::IRContext* ir_context, opt::Function* added_function, opt::Instruction* kill_or_unreachable_inst) const; // A helper for TryToMakeFunctionLivesafe that tries to clamp access chain // indices so that they are guaranteed to be in-bounds. - bool TryToClampAccessChainIndices(opt::IRContext* context, + bool TryToClampAccessChainIndices(opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const; protobufs::TransformationAddFunction message_; diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp index f9585b3bc6..ba45f220a0 100644 --- a/source/fuzz/transformation_add_global_undef.cpp +++ b/source/fuzz/transformation_add_global_undef.cpp @@ -30,26 +30,26 @@ TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id, } bool TransformationAddGlobalUndef::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // A fresh id is required. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } - auto type = context->get_type_mgr()->GetType(message_.type_id()); + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); // The type must exist, and must not be a function type. return type && !type->AsFunction(); } void TransformationAddGlobalUndef::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { - context->module()->AddGlobalValue(MakeUnique( - context, SpvOpUndef, message_.type_id(), message_.fresh_id(), + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ir_context->module()->AddGlobalValue(MakeUnique( + ir_context, SpvOpUndef, message_.type_id(), message_.fresh_id(), opt::Instruction::OperandList())); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const { diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h index 550d9f68fa..c89fe9d5cf 100644 --- a/source/fuzz/transformation_add_global_undef.h +++ b/source/fuzz/transformation_add_global_undef.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -32,12 +32,14 @@ class TransformationAddGlobalUndef : public Transformation { // - |message_.fresh_id| must be fresh // - |message_.type_id| must be the id of a non-function type - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpUndef instruction to the module, with |message_.type_id| as its // type. The instruction has result id |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp index e4f9f7ad6c..c0164280de 100644 --- a/source/fuzz/transformation_add_global_variable.cpp +++ b/source/fuzz/transformation_add_global_variable.cpp @@ -33,14 +33,13 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable( } bool TransformationAddGlobalVariable::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The result id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The type id must correspond to a type. - auto type = context->get_type_mgr()->GetType(message_.type_id()); + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); if (!type) { return false; } @@ -55,7 +54,7 @@ bool TransformationAddGlobalVariable::IsApplicable( } // The initializer id must be the id of a constant. Check this with the // constant manager. - auto constant_id = context->get_constant_mgr()->GetConstantsFromIds( + auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds( {message_.initializer_id()}); if (constant_id.empty()) { return false; @@ -71,7 +70,8 @@ bool TransformationAddGlobalVariable::IsApplicable( } void TransformationAddGlobalVariable::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { opt::Instruction::OperandList input_operands; input_operands.push_back( {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}}); @@ -79,12 +79,12 @@ void TransformationAddGlobalVariable::Apply( input_operands.push_back( {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}); } - context->module()->AddGlobalValue( - MakeUnique(context, SpvOpVariable, message_.type_id(), - message_.fresh_id(), input_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddGlobalValue(MakeUnique( + ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(), + input_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(context)) { + if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(ir_context)) { // Conservatively add this global to the interface of every entry point in // the module. This means that the global is available for other // transformations to use. @@ -94,18 +94,20 @@ void TransformationAddGlobalVariable::Apply( // // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit // this if a more thorough approach to entry point interfaces is taken. - for (auto& entry_point : context->module()->entry_points()) { + for (auto& entry_point : ir_context->module()->entry_points()) { entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}); } } if (message_.value_is_irrelevant()) { - fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); } // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const { @@ -116,11 +118,11 @@ protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const { bool TransformationAddGlobalVariable:: PrivateGlobalsMustBeDeclaredInEntryPointInterfaces( - opt::IRContext* context) { + opt::IRContext* ir_context) { // TODO(afd): We capture the universal environments for which this requirement // holds. The check should be refined on demand for other target // environments. - switch (context->grammar().target_env()) { + switch (ir_context->grammar().target_env()) { case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h index 920ac45dc4..f28af445cd 100644 --- a/source/fuzz/transformation_add_global_variable.h +++ b/source/fuzz/transformation_add_global_variable.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -37,23 +37,25 @@ class TransformationAddGlobalVariable : public Transformation { // class // - |message_.initializer_id| must either be 0 or the id of a constant whose // type is the pointee type of |message_.type_id| - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds a global variable with Private storage class to the module, with type // |message_.type_id| and either no initializer or |message_.initializer_id| // as an initializer, depending on whether |message_.initializer_id| is 0. // The global variable has result id |message_.fresh_id|. // - // If |message_.value_is_irrelevant| holds, adds a corresponding fact to - // |fact_manager|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the + // fact manager in |transformation_context|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; private: static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces( - opt::IRContext* context); + opt::IRContext* ir_context); protobufs::TransformationAddGlobalVariable message_; }; diff --git a/source/fuzz/transformation_add_local_variable.cpp b/source/fuzz/transformation_add_local_variable.cpp index 69e536df16..513624959e 100644 --- a/source/fuzz/transformation_add_local_variable.cpp +++ b/source/fuzz/transformation_add_local_variable.cpp @@ -34,23 +34,22 @@ TransformationAddLocalVariable::TransformationAddLocalVariable( } bool TransformationAddLocalVariable::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The provided id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The pointer type id must indeed correspond to a pointer, and it must have // function storage class. auto type_instruction = - context->get_def_use_mgr()->GetDef(message_.type_id()); + ir_context->get_def_use_mgr()->GetDef(message_.type_id()); if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer || type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) { return false; } // The initializer must... auto initializer_instruction = - context->get_def_use_mgr()->GetDef(message_.initializer_id()); + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); // ... exist, ... if (!initializer_instruction) { return false; @@ -65,17 +64,18 @@ bool TransformationAddLocalVariable::IsApplicable( return false; } // The function to which the local variable is to be added must exist. - return fuzzerutil::FindFunction(context, message_.function_id()); + return fuzzerutil::FindFunction(ir_context, message_.function_id()); } void TransformationAddLocalVariable::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - fuzzerutil::FindFunction(context, message_.function_id()) + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::FindFunction(ir_context, message_.function_id()) ->begin() ->begin() ->InsertBefore(MakeUnique( - context, SpvOpVariable, message_.type_id(), message_.fresh_id(), + ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_STORAGE_CLASS, { @@ -83,9 +83,10 @@ void TransformationAddLocalVariable::Apply( SpvStorageClassFunction}}, {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}}))); if (message_.value_is_irrelevant()) { - fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); } - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationAddLocalVariable::ToMessage() const { diff --git a/source/fuzz/transformation_add_local_variable.h b/source/fuzz/transformation_add_local_variable.h index b8e00ddfb4..646090412b 100644 --- a/source/fuzz/transformation_add_local_variable.h +++ b/source/fuzz/transformation_add_local_variable.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -38,15 +38,17 @@ class TransformationAddLocalVariable : public Transformation { // - |message_.initializer_id| must be the id of a constant with the same // type as the pointer's pointee type // - |message_.function_id| must be the id of a function - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an instruction to the start of |message_.function_id|, of the form: // |message_.fresh_id| = OpVariable |message_.type_id| Function // |message_.initializer_id| - // If |message_.value_is_irrelevant| holds, adds a corresponding fact to - // |fact_manager|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the + // fact manager in |transformation_context|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_no_contraction_decoration.cpp b/source/fuzz/transformation_add_no_contraction_decoration.cpp index 7f22cc22c7..4668534b6f 100644 --- a/source/fuzz/transformation_add_no_contraction_decoration.cpp +++ b/source/fuzz/transformation_add_no_contraction_decoration.cpp @@ -31,10 +31,9 @@ TransformationAddNoContractionDecoration:: } bool TransformationAddNoContractionDecoration::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // |message_.result_id| must be the id of an instruction. - auto instr = context->get_def_use_mgr()->GetDef(message_.result_id()); + auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); if (!instr) { return false; } @@ -43,10 +42,10 @@ bool TransformationAddNoContractionDecoration::IsApplicable( } void TransformationAddNoContractionDecoration::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { // Add a NoContraction decoration targeting |message_.result_id|. - context->get_decoration_mgr()->AddDecoration(message_.result_id(), - SpvDecorationNoContraction); + ir_context->get_decoration_mgr()->AddDecoration(message_.result_id(), + SpvDecorationNoContraction); } protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage() diff --git a/source/fuzz/transformation_add_no_contraction_decoration.h b/source/fuzz/transformation_add_no_contraction_decoration.h index cec1b2cded..27c3a8021f 100644 --- a/source/fuzz/transformation_add_no_contraction_decoration.h +++ b/source/fuzz/transformation_add_no_contraction_decoration.h @@ -15,9 +15,9 @@ #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/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -34,13 +34,15 @@ class TransformationAddNoContractionDecoration : public Transformation { // 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; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) 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; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_array.cpp b/source/fuzz/transformation_add_type_array.cpp index 2074e98a1e..8f5af07fb5 100644 --- a/source/fuzz/transformation_add_type_array.cpp +++ b/source/fuzz/transformation_add_type_array.cpp @@ -32,21 +32,20 @@ TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id, } bool TransformationAddTypeArray::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // A fresh id is required. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } auto element_type = - context->get_type_mgr()->GetType(message_.element_type_id()); + ir_context->get_type_mgr()->GetType(message_.element_type_id()); if (!element_type || element_type->AsFunction()) { // The element type id either does not refer to a type, or refers to a // function type; both are illegal. return false; } auto constant = - context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()}); + ir_context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()}); if (constant.empty()) { // The size id does not refer to a constant. return false; @@ -66,16 +65,17 @@ bool TransformationAddTypeArray::IsApplicable( } void TransformationAddTypeArray::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList in_operands; in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}}); in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}}); - context->module()->AddType(MakeUnique( - context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeArray::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_array.h b/source/fuzz/transformation_add_type_array.h index b6e071827d..5e9b8aaf93 100644 --- a/source/fuzz/transformation_add_type_array.h +++ b/source/fuzz/transformation_add_type_array.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -35,13 +35,15 @@ class TransformationAddTypeArray : public Transformation { // - |message_.element_type_id| must be the id of a non-function type // - |message_.size_id| must be the id of a 32-bit integer constant that is // positive when interpreted as signed. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypeArray instruction to the module, with element type given by // |message_.element_type_id| and size given by |message_.size_id|. The // result id of the instruction is |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_boolean.cpp b/source/fuzz/transformation_add_type_boolean.cpp index b55028a6be..77409a8c95 100644 --- a/source/fuzz/transformation_add_type_boolean.cpp +++ b/source/fuzz/transformation_add_type_boolean.cpp @@ -28,27 +28,27 @@ TransformationAddTypeBoolean::TransformationAddTypeBoolean(uint32_t fresh_id) { } bool TransformationAddTypeBoolean::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // Applicable if there is no bool type already declared in the module. opt::analysis::Bool bool_type; - return context->get_type_mgr()->GetId(&bool_type) == 0; + return ir_context->get_type_mgr()->GetId(&bool_type) == 0; } void TransformationAddTypeBoolean::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList empty_operands; - context->module()->AddType(MakeUnique( - context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeBoolean::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_boolean.h b/source/fuzz/transformation_add_type_boolean.h index 98c1e639c7..5ce5b9a471 100644 --- a/source/fuzz/transformation_add_type_boolean.h +++ b/source/fuzz/transformation_add_type_boolean.h @@ -15,7 +15,6 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_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" @@ -32,11 +31,13 @@ class TransformationAddTypeBoolean : public Transformation { // - |message_.fresh_id| must not be used by the module. // - The module must not yet declare OpTypeBoolean - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds OpTypeBoolean with |message_.fresh_id| as result id. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_float.cpp b/source/fuzz/transformation_add_type_float.cpp index d2af5f885b..80716e14fc 100644 --- a/source/fuzz/transformation_add_type_float.cpp +++ b/source/fuzz/transformation_add_type_float.cpp @@ -30,29 +30,29 @@ TransformationAddTypeFloat::TransformationAddTypeFloat( : message_(message) {} bool TransformationAddTypeFloat::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // Applicable if there is no float type with this width already declared in // the module. opt::analysis::Float float_type(message_.width()); - return context->get_type_mgr()->GetId(&float_type) == 0; + return ir_context->get_type_mgr()->GetId(&float_type) == 0; } void TransformationAddTypeFloat::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList width = { {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}}; - context->module()->AddType(MakeUnique( - context, SpvOpTypeFloat, 0, message_.fresh_id(), width)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeFloat, 0, message_.fresh_id(), width)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeFloat::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_float.h b/source/fuzz/transformation_add_type_float.h index 0fdc8314ef..a8fa0e1044 100644 --- a/source/fuzz/transformation_add_type_float.h +++ b/source/fuzz/transformation_add_type_float.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -33,11 +33,13 @@ class TransformationAddTypeFloat : public Transformation { // - |message_.fresh_id| must not be used by the module // - The module must not contain an OpTypeFloat instruction with width // |message_.width| - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypeFloat instruction to the module with the given width - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_function.cpp b/source/fuzz/transformation_add_type_function.cpp index 4b6717b868..991a28b01d 100644 --- a/source/fuzz/transformation_add_type_function.cpp +++ b/source/fuzz/transformation_add_type_function.cpp @@ -36,19 +36,18 @@ TransformationAddTypeFunction::TransformationAddTypeFunction( } bool TransformationAddTypeFunction::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The result id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The return and argument types must be type ids but not not be function // type ids. - if (!fuzzerutil::IsNonFunctionTypeId(context, message_.return_type_id())) { + if (!fuzzerutil::IsNonFunctionTypeId(ir_context, message_.return_type_id())) { return false; } for (auto argument_type_id : message_.argument_type_id()) { - if (!fuzzerutil::IsNonFunctionTypeId(context, argument_type_id)) { + if (!fuzzerutil::IsNonFunctionTypeId(ir_context, argument_type_id)) { return false; } } @@ -56,7 +55,7 @@ bool TransformationAddTypeFunction::IsApplicable( // exactly the same return and argument type ids. (Note that the type manager // does not allow us to check this, as it does not distinguish between // function types with different but isomorphic pointer argument types.) - for (auto& inst : context->module()->types_values()) { + for (auto& inst : ir_context->module()->types_values()) { if (inst.opcode() != SpvOpTypeFunction) { // Consider only OpTypeFunction instructions. continue; @@ -89,18 +88,19 @@ bool TransformationAddTypeFunction::IsApplicable( } void TransformationAddTypeFunction::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList in_operands; in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}}); for (auto argument_type_id : message_.argument_type_id()) { in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}}); } - context->module()->AddType(MakeUnique( - context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeFunction::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_function.h b/source/fuzz/transformation_add_type_function.h index 3880963d03..f26b2501ca 100644 --- a/source/fuzz/transformation_add_type_function.h +++ b/source/fuzz/transformation_add_type_function.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -39,13 +39,15 @@ class TransformationAddTypeFunction : public Transformation { // - The module must not contain an OpTypeFunction instruction defining a // function type with the signature provided by the given return and // argument types - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypeFunction instruction to the module, with signature given by // |message_.return_type_id| and |message_.argument_type_id|. The result id // for the instruction is |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_int.cpp b/source/fuzz/transformation_add_type_int.cpp index 6f592709a8..a932a5f96c 100644 --- a/source/fuzz/transformation_add_type_int.cpp +++ b/source/fuzz/transformation_add_type_int.cpp @@ -32,30 +32,30 @@ TransformationAddTypeInt::TransformationAddTypeInt(uint32_t fresh_id, } bool TransformationAddTypeInt::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // Applicable if there is no int type with this width and signedness already // declared in the module. opt::analysis::Integer int_type(message_.width(), message_.is_signed()); - return context->get_type_mgr()->GetId(&int_type) == 0; + return ir_context->get_type_mgr()->GetId(&int_type) == 0; } -void TransformationAddTypeInt::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { +void TransformationAddTypeInt::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { opt::Instruction::OperandList in_operands = { {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}, {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.is_signed() ? 1u : 0u}}}; - context->module()->AddType(MakeUnique( - context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeInt::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_int.h b/source/fuzz/transformation_add_type_int.h index 86342d06dd..5c3c9591d8 100644 --- a/source/fuzz/transformation_add_type_int.h +++ b/source/fuzz/transformation_add_type_int.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -33,12 +33,14 @@ class TransformationAddTypeInt : public Transformation { // - |message_.fresh_id| must not be used by the module // - The module must not contain an OpTypeInt instruction with width // |message_.width| and signedness |message.is_signed| - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypeInt instruction to the module with the given width and // signedness. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_matrix.cpp b/source/fuzz/transformation_add_type_matrix.cpp index 07ab7054a0..2c24eaa082 100644 --- a/source/fuzz/transformation_add_type_matrix.cpp +++ b/source/fuzz/transformation_add_type_matrix.cpp @@ -31,15 +31,14 @@ TransformationAddTypeMatrix::TransformationAddTypeMatrix( } bool TransformationAddTypeMatrix::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The result id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The column type must be a floating-point vector. auto column_type = - context->get_type_mgr()->GetType(message_.column_type_id()); + ir_context->get_type_mgr()->GetType(message_.column_type_id()); if (!column_type) { return false; } @@ -48,17 +47,18 @@ bool TransformationAddTypeMatrix::IsApplicable( } void TransformationAddTypeMatrix::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList in_operands; in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}}); in_operands.push_back( {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}}); - context->module()->AddType(MakeUnique( - context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_matrix.h b/source/fuzz/transformation_add_type_matrix.h index 69d638906c..6d0724e6fd 100644 --- a/source/fuzz/transformation_add_type_matrix.h +++ b/source/fuzz/transformation_add_type_matrix.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -33,13 +33,15 @@ class TransformationAddTypeMatrix : public Transformation { // - |message_.fresh_id| must be a fresh id // - |message_.column_type_id| must be the id of a floating-point vector type - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypeMatrix instruction to the module, with column type // |message_.column_type_id| and |message_.column_count| columns, with result // id |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_pointer.cpp b/source/fuzz/transformation_add_type_pointer.cpp index 426985a37e..6cc8171ee1 100644 --- a/source/fuzz/transformation_add_type_pointer.cpp +++ b/source/fuzz/transformation_add_type_pointer.cpp @@ -31,28 +31,29 @@ TransformationAddTypePointer::TransformationAddTypePointer( } bool TransformationAddTypePointer::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The base type must be known. - return context->get_type_mgr()->GetType(message_.base_type_id()) != nullptr; + return ir_context->get_type_mgr()->GetType(message_.base_type_id()) != + nullptr; } void TransformationAddTypePointer::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { // Add the pointer type. opt::Instruction::OperandList in_operands = { {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}}, {SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}}; - context->module()->AddType(MakeUnique( - context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypePointer::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_pointer.h b/source/fuzz/transformation_add_type_pointer.h index 2b9ff77fdb..3b50a293f1 100644 --- a/source/fuzz/transformation_add_type_pointer.h +++ b/source/fuzz/transformation_add_type_pointer.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -34,12 +34,14 @@ class TransformationAddTypePointer : public Transformation { // - |message_.fresh_id| must not be used by the module // - |message_.base_type_id| must be the result id of an OpType[...] // instruction - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypePointer instruction with the given storage class and base // type to the module. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp index 1ae83723dc..6ce5ea11c3 100644 --- a/source/fuzz/transformation_add_type_struct.cpp +++ b/source/fuzz/transformation_add_type_struct.cpp @@ -32,14 +32,13 @@ TransformationAddTypeStruct::TransformationAddTypeStruct( } bool TransformationAddTypeStruct::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // A fresh id is required. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } for (auto member_type : message_.member_type_id()) { - auto type = context->get_type_mgr()->GetType(member_type); + auto type = ir_context->get_type_mgr()->GetType(member_type); if (!type || type->AsFunction()) { // The member type id either does not refer to a type, or refers to a // function type; both are illegal. @@ -50,17 +49,18 @@ bool TransformationAddTypeStruct::IsApplicable( } void TransformationAddTypeStruct::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList in_operands; for (auto member_type : message_.member_type_id()) { in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}}); } - context->module()->AddType(MakeUnique( - context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeStruct::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_struct.h b/source/fuzz/transformation_add_type_struct.h index edf3ec6eef..86a532d266 100644 --- a/source/fuzz/transformation_add_type_struct.h +++ b/source/fuzz/transformation_add_type_struct.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -35,12 +35,14 @@ class TransformationAddTypeStruct : public Transformation { // - |message_.fresh_id| must be a fresh id // - |message_.member_type_id| must be a sequence of non-function type ids - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypeStruct instruction whose field types are given by // |message_.member_type_id|, with result id |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp index 3fdf50b1c8..f7b2fb59f0 100644 --- a/source/fuzz/transformation_add_type_vector.cpp +++ b/source/fuzz/transformation_add_type_vector.cpp @@ -31,13 +31,12 @@ TransformationAddTypeVector::TransformationAddTypeVector( } bool TransformationAddTypeVector::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } auto component_type = - context->get_type_mgr()->GetType(message_.component_type_id()); + ir_context->get_type_mgr()->GetType(message_.component_type_id()); if (!component_type) { return false; } @@ -46,17 +45,18 @@ bool TransformationAddTypeVector::IsApplicable( } void TransformationAddTypeVector::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList in_operands; in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}}); in_operands.push_back( {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}); - context->module()->AddType(MakeUnique( - context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddTypeVector::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_vector.h b/source/fuzz/transformation_add_type_vector.h index af840f5ebe..240f7cce56 100644 --- a/source/fuzz/transformation_add_type_vector.h +++ b/source/fuzz/transformation_add_type_vector.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ #define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -33,13 +33,15 @@ class TransformationAddTypeVector : public Transformation { // - |message_.fresh_id| must be a fresh id // - |message_.component_type_id| must be the id of a scalar type - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpTypeVector instruction to the module, with component type // |message_.component_type_id| and |message_.component_count| components, // with result id |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp index 9c63c1d98a..cd4f22f14b 100644 --- a/source/fuzz/transformation_composite_construct.cpp +++ b/source/fuzz/transformation_composite_construct.cpp @@ -40,14 +40,14 @@ TransformationCompositeConstruct::TransformationCompositeConstruct( } bool TransformationCompositeConstruct::IsApplicable( - opt::IRContext* context, const FactManager& /*fact_manager*/) const { - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { // We require the id for the composite constructor to be unused. return false; } auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); if (!insert_before) { // The instruction before which the composite should be inserted was not // found. @@ -55,7 +55,7 @@ bool TransformationCompositeConstruct::IsApplicable( } auto composite_type = - context->get_type_mgr()->GetType(message_.composite_type_id()); + ir_context->get_type_mgr()->GetType(message_.composite_type_id()); if (!fuzzerutil::IsCompositeType(composite_type)) { // The type must actually be a composite. @@ -64,27 +64,31 @@ bool TransformationCompositeConstruct::IsApplicable( // 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())) { + if (composite_type->AsArray() && + !ComponentsForArrayConstructionAreOK(ir_context, + *composite_type->AsArray())) { return false; } - if (composite_type->AsMatrix() && !ComponentsForMatrixConstructionAreOK( - context, *composite_type->AsMatrix())) { + if (composite_type->AsMatrix() && + !ComponentsForMatrixConstructionAreOK(ir_context, + *composite_type->AsMatrix())) { return false; } - if (composite_type->AsStruct() && !ComponentsForStructConstructionAreOK( - context, *composite_type->AsStruct())) { + if (composite_type->AsStruct() && + !ComponentsForStructConstructionAreOK(ir_context, + *composite_type->AsStruct())) { return false; } - if (composite_type->AsVector() && !ComponentsForVectorConstructionAreOK( - context, *composite_type->AsVector())) { + if (composite_type->AsVector() && + !ComponentsForVectorConstructionAreOK(ir_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()) { - if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, component)) { return false; } @@ -93,13 +97,14 @@ bool TransformationCompositeConstruct::IsApplicable( return true; } -void TransformationCompositeConstruct::Apply(opt::IRContext* context, - FactManager* fact_manager) const { +void TransformationCompositeConstruct::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // Use the base and offset information from the transformation to determine // where in the module a new instruction should be inserted. auto insert_before_inst = - FindInstruction(message_.instruction_to_insert_before(), context); - auto destination_block = context->get_instr_block(insert_before_inst); + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto destination_block = ir_context->get_instr_block(insert_before_inst); auto insert_before = fuzzerutil::GetIteratorForInstruction( destination_block, insert_before_inst); @@ -111,22 +116,22 @@ void TransformationCompositeConstruct::Apply(opt::IRContext* context, // Insert an OpCompositeConstruct instruction. insert_before.InsertBefore(MakeUnique( - context, SpvOpCompositeConstruct, message_.composite_type_id(), + ir_context, SpvOpCompositeConstruct, message_.composite_type_id(), message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); // 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, // except in the case of a vector where a single vector id can span multiple // components. auto composite_type = - context->get_type_mgr()->GetType(message_.composite_type_id()); + ir_context->get_type_mgr()->GetType(message_.composite_type_id()); uint32_t index = 0; for (auto component : message_.component()) { - auto component_type = context->get_type_mgr()->GetType( - context->get_def_use_mgr()->GetDef(component)->type_id()); + auto component_type = ir_context->get_type_mgr()->GetType( + ir_context->get_def_use_mgr()->GetDef(component)->type_id()); if (composite_type->AsVector() && component_type->AsVector()) { // The case where the composite being constructed is a vector and the // component provided for construction is also a vector is special. It @@ -139,24 +144,24 @@ void TransformationCompositeConstruct::Apply(opt::IRContext* context, for (uint32_t subvector_index = 0; subvector_index < component_type->AsVector()->element_count(); subvector_index++) { - fact_manager->AddFactDataSynonym( + transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(component, {subvector_index}), - MakeDataDescriptor(message_.fresh_id(), {index}), context); + MakeDataDescriptor(message_.fresh_id(), {index}), ir_context); index++; } } else { // The other cases are simple: the component is made directly synonymous // with the element of the composite being constructed. - fact_manager->AddFactDataSynonym( + transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(component, {}), - MakeDataDescriptor(message_.fresh_id(), {index}), context); + MakeDataDescriptor(message_.fresh_id(), {index}), ir_context); index++; } } } bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK( - opt::IRContext* context, const opt::analysis::Array& array_type) const { + opt::IRContext* ir_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. @@ -176,13 +181,13 @@ bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK( // 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); + auto inst = ir_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()); + auto component_type = ir_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. @@ -193,7 +198,8 @@ bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK( } bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK( - opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const { + opt::IRContext* ir_context, + const opt::analysis::Matrix& matrix_type) const { if (static_cast(message_.component().size()) != matrix_type.element_count()) { // The number of components must match the number of columns of the matrix. @@ -202,13 +208,13 @@ bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK( // 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); + auto inst = ir_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()); + auto component_type = ir_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. @@ -219,7 +225,8 @@ bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK( } bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK( - opt::IRContext* context, const opt::analysis::Struct& struct_type) const { + opt::IRContext* ir_context, + const opt::analysis::Struct& struct_type) const { if (static_cast(message_.component().size()) != struct_type.element_types().size()) { // The number of components must match the number of fields of the struct. @@ -229,14 +236,14 @@ bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK( // 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]); + auto inst = ir_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()); + auto component_type = ir_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. @@ -247,17 +254,18 @@ bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK( } bool TransformationCompositeConstruct::ComponentsForVectorConstructionAreOK( - opt::IRContext* context, const opt::analysis::Vector& vector_type) const { + opt::IRContext* ir_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); + auto inst = ir_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()); + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); assert(component_type); if (component_type == element_type) { base_element_count++; diff --git a/source/fuzz/transformation_composite_construct.h b/source/fuzz/transformation_composite_construct.h index 5369c4c899..2e55e70f94 100644 --- a/source/fuzz/transformation_composite_construct.h +++ b/source/fuzz/transformation_composite_construct.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ #define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -49,15 +49,17 @@ class TransformationCompositeConstruct : public Transformation { // before 'inst'. // - Each element of |message_.component| must be available directly before // 'inst'. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) 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; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -65,19 +67,22 @@ class TransformationCompositeConstruct : public Transformation { // 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; + opt::IRContext* ir_context, const opt::analysis::Array& array_type) const; // Similar, but for matrices. bool ComponentsForMatrixConstructionAreOK( - opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const; + opt::IRContext* ir_context, + const opt::analysis::Matrix& matrix_type) const; // Similar, but for structs. bool ComponentsForStructConstructionAreOK( - opt::IRContext* context, const opt::analysis::Struct& struct_type) const; + opt::IRContext* ir_context, + const opt::analysis::Struct& struct_type) const; // Similar, but for vectors. bool ComponentsForVectorConstructionAreOK( - opt::IRContext* context, const opt::analysis::Vector& vector_type) const; + opt::IRContext* ir_context, + const opt::analysis::Vector& vector_type) const; protobufs::TransformationCompositeConstruct message_; }; diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp index 5d3a386e28..3dc3953d70 100644 --- a/source/fuzz/transformation_composite_extract.cpp +++ b/source/fuzz/transformation_composite_extract.cpp @@ -40,24 +40,23 @@ TransformationCompositeExtract::TransformationCompositeExtract( } bool TransformationCompositeExtract::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } auto instruction_to_insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); if (!instruction_to_insert_before) { return false; } auto composite_instruction = - context->get_def_use_mgr()->GetDef(message_.composite_id()); + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); if (!composite_instruction) { return false; } - if (auto block = context->get_instr_block(composite_instruction)) { + if (auto block = ir_context->get_instr_block(composite_instruction)) { if (composite_instruction == instruction_to_insert_before || - !context->GetDominatorAnalysis(block->GetParent()) + !ir_context->GetDominatorAnalysis(block->GetParent()) ->Dominates(composite_instruction, instruction_to_insert_before)) { return false; } @@ -66,7 +65,7 @@ bool TransformationCompositeExtract::IsApplicable( "An instruction in a block cannot have a result id but no type id."); auto composite_type = - context->get_type_mgr()->GetType(composite_instruction->type_id()); + ir_context->get_type_mgr()->GetType(composite_instruction->type_id()); if (!composite_type) { return false; } @@ -76,30 +75,33 @@ bool TransformationCompositeExtract::IsApplicable( return false; } - return fuzzerutil::WalkCompositeTypeIndices( - context, composite_instruction->type_id(), message_.index()) != 0; + return fuzzerutil::WalkCompositeTypeIndices(ir_context, + composite_instruction->type_id(), + message_.index()) != 0; } void TransformationCompositeExtract::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { opt::Instruction::OperandList extract_operands; extract_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}}); for (auto an_index : message_.index()) { extract_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {an_index}}); } auto composite_instruction = - context->get_def_use_mgr()->GetDef(message_.composite_id()); + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); auto extracted_type = fuzzerutil::WalkCompositeTypeIndices( - context, composite_instruction->type_id(), message_.index()); + ir_context, composite_instruction->type_id(), message_.index()); - FindInstruction(message_.instruction_to_insert_before(), context) + FindInstruction(message_.instruction_to_insert_before(), ir_context) ->InsertBefore(MakeUnique( - context, SpvOpCompositeExtract, extracted_type, message_.fresh_id(), - extract_operands)); + ir_context, SpvOpCompositeExtract, extracted_type, + message_.fresh_id(), extract_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); // Add the fact that the id storing the extracted element is synonymous with // the index into the structure. @@ -111,8 +113,9 @@ void TransformationCompositeExtract::Apply( MakeDataDescriptor(message_.composite_id(), std::move(indices)); protobufs::DataDescriptor data_descriptor_for_result_id = MakeDataDescriptor(message_.fresh_id(), {}); - fact_manager->AddFactDataSynonym(data_descriptor_for_extracted_element, - data_descriptor_for_result_id, context); + transformation_context->GetFactManager()->AddFactDataSynonym( + data_descriptor_for_extracted_element, data_descriptor_for_result_id, + ir_context); } protobufs::Transformation TransformationCompositeExtract::ToMessage() const { diff --git a/source/fuzz/transformation_composite_extract.h b/source/fuzz/transformation_composite_extract.h index c4c9278ccd..8f52d2269b 100644 --- a/source/fuzz/transformation_composite_extract.h +++ b/source/fuzz/transformation_composite_extract.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ #define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -41,15 +41,17 @@ class TransformationCompositeExtract : public Transformation { // - |message_.index| must be a suitable set of indices for // |message_.composite_id|, i.e. it must be possible to follow this chain // of indices to reach a sub-object of |message_.composite_id| - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an OpCompositeConstruct instruction before the instruction identified // by |message_.instruction_to_insert_before|, that extracts from // |message_.composite_id| via indices |message_.index| into // |message_.fresh_id|. Generates a data synonym fact relating // |message_.fresh_id| to the extracted element. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_context.cpp b/source/fuzz/transformation_context.cpp new file mode 100644 index 0000000000..9c8a90f69d --- /dev/null +++ b/source/fuzz/transformation_context.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_context.h" + +namespace spvtools { +namespace fuzz { + +TransformationContext::TransformationContext( + FactManager* transformation_context, + spv_validator_options validator_options) + : fact_manager_(transformation_context), + validator_options_(validator_options) {} + +TransformationContext::~TransformationContext() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_context.h b/source/fuzz/transformation_context.h new file mode 100644 index 0000000000..37e15a22a1 --- /dev/null +++ b/source/fuzz/transformation_context.h @@ -0,0 +1,56 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ + +#include "source/fuzz/fact_manager.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Encapsulates all information that is required to inform how to apply a +// transformation to a module. +class TransformationContext { + public: + // Constructs a transformation context with a given fact manager and validator + // options. + TransformationContext(FactManager* fact_manager, + spv_validator_options validator_options); + + ~TransformationContext(); + + FactManager* GetFactManager() { return fact_manager_; } + + const FactManager* GetFactManager() const { return fact_manager_; } + + spv_validator_options GetValidatorOptions() const { + return validator_options_; + } + + private: + // Manages facts that inform whether transformations can be applied, and that + // are produced by applying transformations. + FactManager* fact_manager_; + + // Options to control validation when deciding whether transformations can be + // applied. + spv_validator_options validator_options_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ diff --git a/source/fuzz/transformation_copy_object.cpp b/source/fuzz/transformation_copy_object.cpp index bfdced37fe..7b5b5c9e03 100644 --- a/source/fuzz/transformation_copy_object.cpp +++ b/source/fuzz/transformation_copy_object.cpp @@ -38,22 +38,22 @@ TransformationCopyObject::TransformationCopyObject( } bool TransformationCopyObject::IsApplicable( - opt::IRContext* context, const FactManager& /*fact_manager*/) const { - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { // We require the id for the object copy to be unused. return false; } // The id of the object to be copied must exist - auto object_inst = context->get_def_use_mgr()->GetDef(message_.object()); + auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object()); if (!object_inst) { return false; } - if (!fuzzerutil::CanMakeSynonymOf(context, object_inst)) { + if (!fuzzerutil::CanMakeSynonymOf(ir_context, object_inst)) { return false; } auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); if (!insert_before) { // The instruction before which the copy should be inserted was not found. return false; @@ -66,17 +66,18 @@ bool TransformationCopyObject::IsApplicable( // |message_object| must be available directly before the point where we want // to add the copy. - return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.object()); } -void TransformationCopyObject::Apply(opt::IRContext* context, - FactManager* fact_manager) const { - auto object_inst = context->get_def_use_mgr()->GetDef(message_.object()); +void TransformationCopyObject::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object()); assert(object_inst && "The object to be copied must exist."); auto insert_before_inst = - FindInstruction(message_.instruction_to_insert_before(), context); - auto destination_block = context->get_instr_block(insert_before_inst); + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto destination_block = ir_context->get_instr_block(insert_before_inst); assert(destination_block && "The base instruction must be in a block."); auto insert_before = fuzzerutil::GetIteratorForInstruction( destination_block, insert_before_inst); @@ -86,18 +87,21 @@ void TransformationCopyObject::Apply(opt::IRContext* context, opt::Instruction::OperandList operands = { {SPV_OPERAND_TYPE_ID, {message_.object()}}}; insert_before->InsertBefore(MakeUnique( - context, SpvOp::SpvOpCopyObject, object_inst->type_id(), + ir_context, SpvOp::SpvOpCopyObject, object_inst->type_id(), message_.fresh_id(), operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); - fact_manager->AddFactDataSynonym(MakeDataDescriptor(message_.object(), {}), - MakeDataDescriptor(message_.fresh_id(), {}), - context); + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.object(), {}), + MakeDataDescriptor(message_.fresh_id(), {}), ir_context); - if (fact_manager->PointeeValueIsIrrelevant(message_.object())) { - fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id()); + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + message_.object())) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); } } diff --git a/source/fuzz/transformation_copy_object.h b/source/fuzz/transformation_copy_object.h index 9e9c26a341..80d57aebe0 100644 --- a/source/fuzz/transformation_copy_object.h +++ b/source/fuzz/transformation_copy_object.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_ #define SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -49,19 +49,21 @@ class TransformationCopyObject : public Transformation { // - |message_.object| must be available directly before 'inst'. // - |message_.object| must not be a null pointer or undefined pointer (so as // to make it legal to load from copied pointers). - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // - 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 |fact_manager|. + // is added to the fact manager in |transformation_context|. // - If |message_.object| is a pointer whose pointee value is known to be - // irrelevant, the analogous fact is added to |fact_manager| about - // |message_.fresh_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + // irrelevant, the analogous fact is added to the fact manager in + // |transformation_context| about |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp index 21b67f66e0..5c3141714c 100644 --- a/source/fuzz/transformation_equation_instruction.cpp +++ b/source/fuzz/transformation_equation_instruction.cpp @@ -37,40 +37,40 @@ TransformationEquationInstruction::TransformationEquationInstruction( } bool TransformationEquationInstruction::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The result id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The instruction to insert before must exist. auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); if (!insert_before) { return false; } // The input ids must all exist, not be OpUndef, and be available before this // instruction. for (auto id : message_.in_operand_id()) { - auto inst = context->get_def_use_mgr()->GetDef(id); + auto inst = ir_context->get_def_use_mgr()->GetDef(id); if (!inst) { return false; } if (inst->opcode() == SpvOpUndef) { return false; } - if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, id)) { return false; } } - return MaybeGetResultType(context) != 0; + return MaybeGetResultType(ir_context) != 0; } void TransformationEquationInstruction::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); opt::Instruction::OperandList in_operands; std::vector rhs_id; @@ -79,16 +79,16 @@ void TransformationEquationInstruction::Apply( rhs_id.push_back(id); } - FindInstruction(message_.instruction_to_insert_before(), context) + FindInstruction(message_.instruction_to_insert_before(), ir_context) ->InsertBefore(MakeUnique( - context, static_cast(message_.opcode()), - MaybeGetResultType(context), message_.fresh_id(), in_operands)); + ir_context, static_cast(message_.opcode()), + MaybeGetResultType(ir_context), message_.fresh_id(), in_operands)); - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); - fact_manager->AddFactIdEquation(message_.fresh_id(), - static_cast(message_.opcode()), rhs_id, - context); + transformation_context->GetFactManager()->AddFactIdEquation( + message_.fresh_id(), static_cast(message_.opcode()), rhs_id, + ir_context); } protobufs::Transformation TransformationEquationInstruction::ToMessage() const { @@ -98,7 +98,7 @@ protobufs::Transformation TransformationEquationInstruction::ToMessage() const { } uint32_t TransformationEquationInstruction::MaybeGetResultType( - opt::IRContext* context) const { + opt::IRContext* ir_context) const { switch (static_cast(message_.opcode())) { case SpvOpIAdd: case SpvOpISub: { @@ -108,13 +108,13 @@ uint32_t TransformationEquationInstruction::MaybeGetResultType( uint32_t first_operand_width = 0; uint32_t first_operand_type_id = 0; for (uint32_t index = 0; index < 2; index++) { - auto operand_inst = - context->get_def_use_mgr()->GetDef(message_.in_operand_id(index)); + auto operand_inst = ir_context->get_def_use_mgr()->GetDef( + message_.in_operand_id(index)); if (!operand_inst || !operand_inst->type_id()) { return 0; } auto operand_type = - context->get_type_mgr()->GetType(operand_inst->type_id()); + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); if (!(operand_type->AsInteger() || (operand_type->AsVector() && operand_type->AsVector()->element_type()->AsInteger()))) { @@ -144,12 +144,12 @@ uint32_t TransformationEquationInstruction::MaybeGetResultType( return 0; } auto operand_inst = - context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); if (!operand_inst || !operand_inst->type_id()) { return 0; } auto operand_type = - context->get_type_mgr()->GetType(operand_inst->type_id()); + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); if (!(operand_type->AsBool() || (operand_type->AsVector() && operand_type->AsVector()->element_type()->AsBool()))) { @@ -162,12 +162,12 @@ uint32_t TransformationEquationInstruction::MaybeGetResultType( return 0; } auto operand_inst = - context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); if (!operand_inst || !operand_inst->type_id()) { return 0; } auto operand_type = - context->get_type_mgr()->GetType(operand_inst->type_id()); + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); if (!(operand_type->AsInteger() || (operand_type->AsVector() && operand_type->AsVector()->element_type()->AsInteger()))) { diff --git a/source/fuzz/transformation_equation_instruction.h b/source/fuzz/transformation_equation_instruction.h index 2456ba5092..7eec9c6593 100644 --- a/source/fuzz/transformation_equation_instruction.h +++ b/source/fuzz/transformation_equation_instruction.h @@ -17,9 +17,9 @@ #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -44,8 +44,9 @@ class TransformationEquationInstruction : public Transformation { // equations, the types of the ids in |message_.in_operand_id| must be // suitable for use with this opcode, and the module must contain an // appropriate result type id. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an instruction to the module, right before // |message_.instruction_to_insert_before|, of the form: @@ -56,7 +57,8 @@ class TransformationEquationInstruction : public Transformation { // compatible with the opcode and input operands. // // The fact manager is also updated to inform it of this equation fact. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -65,7 +67,7 @@ class TransformationEquationInstruction : public Transformation { // in |message_.in_operand_id| are compatible, and that the module contains // an appropriate result type id. If all is well, the result type id is // returned. Otherwise, 0 is returned. - uint32_t MaybeGetResultType(opt::IRContext* context) const; + uint32_t MaybeGetResultType(opt::IRContext* ir_context) const; protobufs::TransformationEquationInstruction message_; }; diff --git a/source/fuzz/transformation_function_call.cpp b/source/fuzz/transformation_function_call.cpp index cea8537157..432634d382 100644 --- a/source/fuzz/transformation_function_call.cpp +++ b/source/fuzz/transformation_function_call.cpp @@ -39,25 +39,26 @@ TransformationFunctionCall::TransformationFunctionCall( } bool TransformationFunctionCall::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& fact_manager) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // The result id must be fresh - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The function must exist - auto callee_inst = context->get_def_use_mgr()->GetDef(message_.callee_id()); + auto callee_inst = + ir_context->get_def_use_mgr()->GetDef(message_.callee_id()); if (!callee_inst || callee_inst->opcode() != SpvOpFunction) { return false; } // The function must not be an entry point - if (fuzzerutil::FunctionIsEntryPoint(context, message_.callee_id())) { + if (fuzzerutil::FunctionIsEntryPoint(ir_context, message_.callee_id())) { return false; } - auto callee_type_inst = context->get_def_use_mgr()->GetDef( + auto callee_type_inst = ir_context->get_def_use_mgr()->GetDef( callee_inst->GetSingleWordInOperand(1)); assert(callee_type_inst->opcode() == SpvOpTypeFunction && "Bad function type."); @@ -73,7 +74,7 @@ bool TransformationFunctionCall::IsApplicable( // The instruction descriptor must refer to a position where it is valid to // insert the call auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); if (!insert_before) { return false; } @@ -82,13 +83,15 @@ bool TransformationFunctionCall::IsApplicable( return false; } - auto block = context->get_instr_block(insert_before); + auto block = ir_context->get_instr_block(insert_before); auto enclosing_function = block->GetParent(); // If the block is not dead, the function must be livesafe - bool block_is_dead = fact_manager.BlockIsDead(block->id()); + bool block_is_dead = + transformation_context.GetFactManager()->BlockIsDead(block->id()); if (!block_is_dead && - !fact_manager.FunctionIsLivesafe(message_.callee_id())) { + !transformation_context.GetFactManager()->FunctionIsLivesafe( + message_.callee_id())) { return false; } @@ -98,7 +101,7 @@ bool TransformationFunctionCall::IsApplicable( arg_index < static_cast(message_.argument_id().size()); arg_index++) { opt::Instruction* arg_inst = - context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index)); + ir_context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index)); if (!arg_inst) { // The given argument does not correspond to an instruction. return false; @@ -112,7 +115,7 @@ bool TransformationFunctionCall::IsApplicable( return false; } opt::Instruction* arg_type_inst = - context->get_def_use_mgr()->GetDef(arg_inst->type_id()); + ir_context->get_def_use_mgr()->GetDef(arg_inst->type_id()); if (arg_type_inst->opcode() == SpvOpTypePointer) { switch (arg_inst->opcode()) { case SpvOpFunctionParameter: @@ -124,7 +127,8 @@ bool TransformationFunctionCall::IsApplicable( return false; } if (!block_is_dead && - !fact_manager.PointeeValueIsIrrelevant(arg_inst->result_id())) { + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + arg_inst->result_id())) { // This is not a dead block, so pointer parameters passed to the called // function might really have their contents modified. We thus require // such pointers to be to arbitrary-valued variables, which this is not. @@ -134,7 +138,7 @@ bool TransformationFunctionCall::IsApplicable( // The argument id needs to be available (according to dominance rules) at // the point where the call will occur. - if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, arg_inst->result_id())) { return false; } @@ -146,19 +150,19 @@ bool TransformationFunctionCall::IsApplicable( return false; } // Ensure the call would not lead to indirect recursion. - return !CallGraph(context) + return !CallGraph(ir_context) .GetIndirectCallees(message_.callee_id()) .count(block->GetParent()->result_id()); } void TransformationFunctionCall::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { // Update the module's bound to reflect the fresh id for the result of the // function call. - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // Get the return type of the function being called. uint32_t return_type = - context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id(); + ir_context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id(); // Populate the operands to the call instruction, with the function id and the // arguments. opt::Instruction::OperandList operands; @@ -167,12 +171,12 @@ void TransformationFunctionCall::Apply( operands.push_back({SPV_OPERAND_TYPE_ID, {arg}}); } // Insert the function call before the instruction specified in the message. - FindInstruction(message_.instruction_to_insert_before(), context) - ->InsertBefore( - MakeUnique(context, SpvOpFunctionCall, return_type, - message_.fresh_id(), operands)); + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpFunctionCall, return_type, message_.fresh_id(), + operands)); // Invalidate all analyses since we have changed the module. - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationFunctionCall::ToMessage() const { diff --git a/source/fuzz/transformation_function_call.h b/source/fuzz/transformation_function_call.h index a9ae5bee3c..4ad7db13df 100644 --- a/source/fuzz/transformation_function_call.h +++ b/source/fuzz/transformation_function_call.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ #define SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -44,14 +44,16 @@ class TransformationFunctionCall : public Transformation { // - If the insertion point is not in a dead block then |message_function_id| // must refer to a livesafe function, and every pointer argument in // |message_.arg_id| must refer to an arbitrary-valued variable - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an instruction of the form: // |fresh_id| = OpFunctionCall %type |callee_id| |arg_id...| // before |instruction_to_insert_before|, where %type is the return type of // |callee_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp index 4cba37da4e..a260c33583 100644 --- a/source/fuzz/transformation_load.cpp +++ b/source/fuzz/transformation_load.cpp @@ -34,20 +34,19 @@ TransformationLoad::TransformationLoad( } bool TransformationLoad::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The result id must be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The pointer must exist and have a type. - auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id()); + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); if (!pointer || !pointer->type_id()) { return false; } // The type must indeed be a pointer type. - auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id()); + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); assert(pointer_type && "Type id must be defined."); if (pointer_type->opcode() != SpvOpTypePointer) { return false; @@ -65,7 +64,7 @@ bool TransformationLoad::IsApplicable( // Determine which instruction we should be inserting before. auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); // It must exist, ... if (!insert_before) { return false; @@ -76,21 +75,21 @@ bool TransformationLoad::IsApplicable( } // The pointer needs to be available at the insertion point. - return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.pointer_id()); } -void TransformationLoad::Apply(opt::IRContext* context, - spvtools::fuzz::FactManager* /*unused*/) const { +void TransformationLoad::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( - context, fuzzerutil::GetTypeId(context, message_.pointer_id())); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - FindInstruction(message_.instruction_to_insert_before(), context) + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + FindInstruction(message_.instruction_to_insert_before(), ir_context) ->InsertBefore(MakeUnique( - context, SpvOpLoad, result_type, message_.fresh_id(), + ir_context, SpvOpLoad, result_type, message_.fresh_id(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}))); - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationLoad::ToMessage() const { diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h index ff99016734..4c7c00b6d3 100644 --- a/source/fuzz/transformation_load.h +++ b/source/fuzz/transformation_load.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ #define SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -37,15 +37,17 @@ class TransformationLoad : public Transformation { // - |message_.instruction_to_insert_before| must identify an instruction // before which it is valid to insert an OpLoad, and where // |message_.pointer_id| is available (according to dominance rules) - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an instruction of the form: // |message_.fresh_id| = OpLoad %type |message_.pointer_id| // before the instruction identified by // |message_.instruction_to_insert_before|, where %type is the pointer's // pointee type. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp index 316e80df3f..68ac09222c 100644 --- a/source/fuzz/transformation_merge_blocks.cpp +++ b/source/fuzz/transformation_merge_blocks.cpp @@ -29,40 +29,41 @@ TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) { } bool TransformationMergeBlocks::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { - auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id()); + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto second_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); // The given block must exist. if (!second_block) { return false; } // The block must have just one predecessor. - auto predecessors = context->cfg()->preds(second_block->id()); + auto predecessors = ir_context->cfg()->preds(second_block->id()); if (predecessors.size() != 1) { return false; } - auto first_block = context->cfg()->block(predecessors.at(0)); + auto first_block = ir_context->cfg()->block(predecessors.at(0)); - return opt::blockmergeutil::CanMergeWithSuccessor(context, first_block); + return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block); } -void TransformationMergeBlocks::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { - auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id()); - auto first_block = - context->cfg()->block(context->cfg()->preds(second_block->id()).at(0)); +void TransformationMergeBlocks::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + auto second_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); + auto first_block = ir_context->cfg()->block( + ir_context->cfg()->preds(second_block->id()).at(0)); auto function = first_block->GetParent(); // We need an iterator pointing to the predecessor, hence the loop. for (auto bi = function->begin(); bi != function->end(); ++bi) { if (bi->id() == first_block->id()) { - assert(opt::blockmergeutil::CanMergeWithSuccessor(context, &*bi) && + assert(opt::blockmergeutil::CanMergeWithSuccessor(ir_context, &*bi) && "Because 'Apply' should only be invoked if 'IsApplicable' holds, " "it must be possible to merge |bi| with its successor."); - opt::blockmergeutil::MergeWithSuccessor(context, function, bi); + opt::blockmergeutil::MergeWithSuccessor(ir_context, function, bi); // Invalidate all analyses, since we have changed the module // significantly. - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); return; } } diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h index 86216db3ba..1dc16d2f8b 100644 --- a/source/fuzz/transformation_merge_blocks.h +++ b/source/fuzz/transformation_merge_blocks.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ #define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -35,12 +35,14 @@ class TransformationMergeBlocks : public Transformation { // - b must be the sole successor of a // - Replacing a with the merge of a and b (and removing b) must lead to a // valid module - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // The contents of b are merged into a, and a's terminator is replaced with // the terminator of b. Block b is removed from the module. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_move_block_down.cpp b/source/fuzz/transformation_move_block_down.cpp index f181855fda..6c71ab70b5 100644 --- a/source/fuzz/transformation_move_block_down.cpp +++ b/source/fuzz/transformation_move_block_down.cpp @@ -28,10 +28,10 @@ TransformationMoveBlockDown::TransformationMoveBlockDown(uint32_t id) { } bool TransformationMoveBlockDown::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // Go through every block in every function, looking for a block whose id // matches that of the block we want to consider moving down. - for (auto& function : *context->module()) { + for (auto& function : *ir_context->module()) { for (auto block_it = function.begin(); block_it != function.end(); ++block_it) { if (block_it->id() == message_.block_id()) { @@ -43,7 +43,7 @@ bool TransformationMoveBlockDown::IsApplicable( } // Record the block we would like to consider moving down. opt::BasicBlock* block_matching_id = &*block_it; - if (!context->GetDominatorAnalysis(&function)->IsReachable( + if (!ir_context->GetDominatorAnalysis(&function)->IsReachable( block_matching_id)) { // The block is not reachable. We are not allowed to move it down. return false; @@ -60,7 +60,7 @@ bool TransformationMoveBlockDown::IsApplicable( opt::BasicBlock* next_block_in_program_order = &*block_it; // We can move the block of interest down if and only if it does not // dominate the block that comes next. - return !context->GetDominatorAnalysis(&function)->Dominates( + return !ir_context->GetDominatorAnalysis(&function)->Dominates( block_matching_id, next_block_in_program_order); } } @@ -71,11 +71,11 @@ bool TransformationMoveBlockDown::IsApplicable( return false; } -void TransformationMoveBlockDown::Apply(opt::IRContext* context, - FactManager* /*unused*/) const { +void TransformationMoveBlockDown::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { // Go through every block in every function, looking for a block whose id // matches that of the block we want to move down. - for (auto& function : *context->module()) { + for (auto& function : *ir_context->module()) { for (auto block_it = function.begin(); block_it != function.end(); ++block_it) { if (block_it->id() == message_.block_id()) { @@ -87,7 +87,7 @@ void TransformationMoveBlockDown::Apply(opt::IRContext* context, // For performance, it is vital to keep the dominator analysis valid // (which due to https://github.com/KhronosGroup/SPIRV-Tools/issues/2889 // requires keeping the CFG analysis valid). - context->InvalidateAnalysesExceptFor( + ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisDefUse | opt::IRContext::Analysis::kAnalysisCFG | opt::IRContext::Analysis::kAnalysisDominatorAnalysis); diff --git a/source/fuzz/transformation_move_block_down.h b/source/fuzz/transformation_move_block_down.h index fd1584a357..7551c38224 100644 --- a/source/fuzz/transformation_move_block_down.h +++ b/source/fuzz/transformation_move_block_down.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ #define SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -35,12 +35,14 @@ class TransformationMoveBlockDown : public Transformation { // in a function. // - b must not dominate the block that follows it in program order. // - b must be reachable. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // The block with id |message_.block_id| is moved down; i.e. the program order // between it and the block that follows it is swapped. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 01d1c45834..117cdc6f80 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -70,72 +70,71 @@ TransformationOutlineFunction::TransformationOutlineFunction( } bool TransformationOutlineFunction::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { std::set ids_used_by_this_transformation; // The various new ids used by the transformation must be fresh and distinct. if (!CheckIdIsFreshAndNotUsedByThisTransformation( - message_.new_function_struct_return_type_id(), context, + message_.new_function_struct_return_type_id(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - message_.new_function_type_id(), context, + message_.new_function_type_id(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - message_.new_function_id(), context, + message_.new_function_id(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - message_.new_function_region_entry_block(), context, + message_.new_function_region_entry_block(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - message_.new_caller_result_id(), context, + message_.new_caller_result_id(), ir_context, &ids_used_by_this_transformation)) { return false; } if (!CheckIdIsFreshAndNotUsedByThisTransformation( - message_.new_callee_result_id(), context, + message_.new_callee_result_id(), ir_context, &ids_used_by_this_transformation)) { return false; } for (auto& pair : message_.input_id_to_fresh_id()) { if (!CheckIdIsFreshAndNotUsedByThisTransformation( - pair.second(), context, &ids_used_by_this_transformation)) { + pair.second(), ir_context, &ids_used_by_this_transformation)) { return false; } } for (auto& pair : message_.output_id_to_fresh_id()) { if (!CheckIdIsFreshAndNotUsedByThisTransformation( - pair.second(), context, &ids_used_by_this_transformation)) { + pair.second(), ir_context, &ids_used_by_this_transformation)) { return false; } } // The entry and exit block ids must indeed refer to blocks. for (auto block_id : {message_.entry_block(), message_.exit_block()}) { - auto block_label = context->get_def_use_mgr()->GetDef(block_id); + auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id); if (!block_label || block_label->opcode() != SpvOpLabel) { return false; } } - auto entry_block = context->cfg()->block(message_.entry_block()); - auto exit_block = context->cfg()->block(message_.exit_block()); + auto entry_block = ir_context->cfg()->block(message_.entry_block()); + auto exit_block = ir_context->cfg()->block(message_.exit_block()); // The entry block cannot start with OpVariable - this would mean that // outlining would remove a variable from the function containing the region @@ -151,7 +150,7 @@ bool TransformationOutlineFunction::IsApplicable( // For simplicity, we do not allow the exit block to be a merge block or // continue target. - if (fuzzerutil::IsMergeOrContinue(context, exit_block->id())) { + if (fuzzerutil::IsMergeOrContinue(ir_context, exit_block->id())) { return false; } @@ -169,14 +168,14 @@ bool TransformationOutlineFunction::IsApplicable( // The entry block must dominate the exit block. auto dominator_analysis = - context->GetDominatorAnalysis(entry_block->GetParent()); + ir_context->GetDominatorAnalysis(entry_block->GetParent()); if (!dominator_analysis->Dominates(entry_block, exit_block)) { return false; } // The exit block must post-dominate the entry block. auto postdominator_analysis = - context->GetPostDominatorAnalysis(entry_block->GetParent()); + ir_context->GetPostDominatorAnalysis(entry_block->GetParent()); if (!postdominator_analysis->Dominates(exit_block, entry_block)) { return false; } @@ -184,8 +183,9 @@ bool TransformationOutlineFunction::IsApplicable( // Find all the blocks dominated by |message_.entry_block| and post-dominated // by |message_.exit_block|. auto region_set = GetRegionBlocks( - context, entry_block = context->cfg()->block(message_.entry_block()), - exit_block = context->cfg()->block(message_.exit_block())); + ir_context, + entry_block = ir_context->cfg()->block(message_.entry_block()), + exit_block = ir_context->cfg()->block(message_.exit_block())); // Check whether |region_set| really is a single-entry single-exit region, and // also check whether structured control flow constructs and their merge @@ -210,9 +210,9 @@ bool TransformationOutlineFunction::IsApplicable( // see whether all of the block's successors are in the region. If they // are not, the region is not single-entry single-exit. bool all_successors_in_region = true; - block.WhileEachSuccessorLabel([&all_successors_in_region, context, + block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context, ®ion_set](uint32_t successor) -> bool { - if (region_set.count(context->cfg()->block(successor)) == 0) { + if (region_set.count(ir_context->cfg()->block(successor)) == 0) { all_successors_in_region = false; return false; } @@ -227,7 +227,8 @@ bool TransformationOutlineFunction::IsApplicable( // The block is a loop or selection header -- the header and its // associated merge block had better both be in the region or both be // outside the region. - auto merge_block = context->cfg()->block(merge->GetSingleWordOperand(0)); + auto merge_block = + ir_context->cfg()->block(merge->GetSingleWordOperand(0)); if (region_set.count(&block) != region_set.count(merge_block)) { return false; } @@ -236,7 +237,7 @@ bool TransformationOutlineFunction::IsApplicable( if (auto loop_merge = block.GetLoopMergeInst()) { // Similar to the above, but for the continue target of a loop. auto continue_target = - context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1)); if (continue_target != exit_block && region_set.count(&block) != region_set.count(continue_target)) { return false; @@ -248,7 +249,7 @@ bool TransformationOutlineFunction::IsApplicable( // used inside the region, ... std::map input_id_to_fresh_id_map = PairSequenceToMap(message_.input_id_to_fresh_id()); - for (auto id : GetRegionInputIds(context, region_set, exit_block)) { + for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) { // There needs to be a corresponding fresh id to be used as a function // parameter. if (input_id_to_fresh_id_map.count(id) == 0) { @@ -256,8 +257,8 @@ bool TransformationOutlineFunction::IsApplicable( } // Furthermore, if the input id has pointer type it must be an OpVariable // or OpFunctionParameter. - auto input_id_inst = context->get_def_use_mgr()->GetDef(id); - if (context->get_def_use_mgr() + auto input_id_inst = ir_context->get_def_use_mgr()->GetDef(id); + if (ir_context->get_def_use_mgr() ->GetDef(input_id_inst->type_id()) ->opcode() == SpvOpTypePointer) { switch (input_id_inst->opcode()) { @@ -277,7 +278,7 @@ bool TransformationOutlineFunction::IsApplicable( // can hold the value for this id computed in the outlined function. std::map output_id_to_fresh_id_map = PairSequenceToMap(message_.output_id_to_fresh_id()); - for (auto id : GetRegionOutputIds(context, region_set, exit_block)) { + for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) { if (output_id_to_fresh_id_map.count(id) == 0) { return false; } @@ -287,25 +288,26 @@ bool TransformationOutlineFunction::IsApplicable( } void TransformationOutlineFunction::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // The entry block for the region before outlining. auto original_region_entry_block = - context->cfg()->block(message_.entry_block()); + ir_context->cfg()->block(message_.entry_block()); // The exit block for the region before outlining. auto original_region_exit_block = - context->cfg()->block(message_.exit_block()); + ir_context->cfg()->block(message_.exit_block()); // The single-entry single-exit region defined by |message_.entry_block| and // |message_.exit_block|. std::set region_blocks = GetRegionBlocks( - context, original_region_entry_block, original_region_exit_block); + ir_context, original_region_entry_block, original_region_exit_block); // Input and output ids for the region being outlined. std::vector region_input_ids = - GetRegionInputIds(context, region_blocks, original_region_exit_block); + GetRegionInputIds(ir_context, region_blocks, original_region_exit_block); std::vector region_output_ids = - GetRegionOutputIds(context, region_blocks, original_region_exit_block); + GetRegionOutputIds(ir_context, region_blocks, original_region_exit_block); // Maps from input and output ids to fresh ids. std::map input_id_to_fresh_id_map = @@ -313,14 +315,14 @@ void TransformationOutlineFunction::Apply( std::map output_id_to_fresh_id_map = PairSequenceToMap(message_.output_id_to_fresh_id()); - UpdateModuleIdBoundForFreshIds(context, input_id_to_fresh_id_map, + UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map, output_id_to_fresh_id_map); // Construct a map that associates each output id with its type id. std::map output_id_to_type_id; for (uint32_t output_id : region_output_ids) { output_id_to_type_id[output_id] = - context->get_def_use_mgr()->GetDef(output_id)->type_id(); + ir_context->get_def_use_mgr()->GetDef(output_id)->type_id(); } // The region will be collapsed to a single block that calls a function @@ -331,53 +333,55 @@ void TransformationOutlineFunction::Apply( // collapsed block later. std::unique_ptr cloned_exit_block_terminator = std::unique_ptr( - original_region_exit_block->terminator()->Clone(context)); + original_region_exit_block->terminator()->Clone(ir_context)); std::unique_ptr cloned_exit_block_merge = original_region_exit_block->GetMergeInst() ? std::unique_ptr( - original_region_exit_block->GetMergeInst()->Clone(context)) + original_region_exit_block->GetMergeInst()->Clone(ir_context)) : nullptr; // Make a function prototype for the outlined function, which involves // figuring out its required type. - std::unique_ptr outlined_function = - PrepareFunctionPrototype(region_input_ids, region_output_ids, - input_id_to_fresh_id_map, context, fact_manager); + std::unique_ptr outlined_function = PrepareFunctionPrototype( + region_input_ids, region_output_ids, input_id_to_fresh_id_map, ir_context, + transformation_context); // If the original function was livesafe, the new function should also be // livesafe. - if (fact_manager->FunctionIsLivesafe( + if (transformation_context->GetFactManager()->FunctionIsLivesafe( original_region_entry_block->GetParent()->result_id())) { - fact_manager->AddFactFunctionIsLivesafe(message_.new_function_id()); + transformation_context->GetFactManager()->AddFactFunctionIsLivesafe( + message_.new_function_id()); } // Adapt the region to be outlined so that its input ids are replaced with the // ids of the outlined function's input parameters, and so that output ids // are similarly remapped. RemapInputAndOutputIdsInRegion( - context, *original_region_exit_block, region_blocks, region_input_ids, + ir_context, *original_region_exit_block, region_blocks, region_input_ids, region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map); // Fill out the body of the outlined function according to the region that is // being outlined. - PopulateOutlinedFunction(*original_region_entry_block, - *original_region_exit_block, region_blocks, - region_output_ids, output_id_to_fresh_id_map, - context, outlined_function.get(), fact_manager); + PopulateOutlinedFunction( + *original_region_entry_block, *original_region_exit_block, region_blocks, + region_output_ids, output_id_to_fresh_id_map, ir_context, + outlined_function.get(), transformation_context); // Collapse the region that has been outlined into a function down to a single // block that calls said function. ShrinkOriginalRegion( - context, region_blocks, region_input_ids, region_output_ids, + ir_context, region_blocks, region_input_ids, region_output_ids, output_id_to_type_id, outlined_function->type_id(), std::move(cloned_exit_block_merge), std::move(cloned_exit_block_terminator), original_region_entry_block); // Add the outlined function to the module. - context->module()->AddFunction(std::move(outlined_function)); + ir_context->module()->AddFunction(std::move(outlined_function)); // Major surgery has been conducted on the module, so invalidate all analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationOutlineFunction::ToMessage() const { @@ -387,30 +391,31 @@ protobufs::Transformation TransformationOutlineFunction::ToMessage() const { } std::vector TransformationOutlineFunction::GetRegionInputIds( - opt::IRContext* context, const std::set& region_set, + opt::IRContext* ir_context, const std::set& region_set, opt::BasicBlock* region_exit_block) { std::vector result; auto enclosing_function = region_exit_block->GetParent(); // Consider each parameter of the function containing the region. - enclosing_function->ForEachParam([context, ®ion_set, &result]( - opt::Instruction* function_parameter) { - // Consider every use of the parameter. - context->get_def_use_mgr()->WhileEachUse( - function_parameter, [context, function_parameter, ®ion_set, &result]( - opt::Instruction* use, uint32_t /*unused*/) { - // Get the block, if any, in which the parameter is used. - auto use_block = context->get_instr_block(use); - // If the use is in a block that lies within the region, the - // parameter is an input id for the region. - if (use_block && region_set.count(use_block) != 0) { - result.push_back(function_parameter->result_id()); - return false; - } - return true; - }); - }); + enclosing_function->ForEachParam( + [ir_context, ®ion_set, &result](opt::Instruction* function_parameter) { + // Consider every use of the parameter. + ir_context->get_def_use_mgr()->WhileEachUse( + function_parameter, + [ir_context, function_parameter, ®ion_set, &result]( + opt::Instruction* use, uint32_t /*unused*/) { + // Get the block, if any, in which the parameter is used. + auto use_block = ir_context->get_instr_block(use); + // If the use is in a block that lies within the region, the + // parameter is an input id for the region. + if (use_block && region_set.count(use_block) != 0) { + result.push_back(function_parameter->result_id()); + return false; + } + return true; + }); + }); // Consider all definitions in the function that might turn out to be input // ids. @@ -430,15 +435,15 @@ std::vector TransformationOutlineFunction::GetRegionInputIds( // Consider each candidate input id to check whether it is used in the // region. for (auto& inst : candidate_input_ids_for_block) { - context->get_def_use_mgr()->WhileEachUse( + ir_context->get_def_use_mgr()->WhileEachUse( inst, - [context, &inst, region_exit_block, ®ion_set, &result]( + [ir_context, &inst, region_exit_block, ®ion_set, &result]( opt::Instruction* use, uint32_t /*unused*/) -> bool { // Find the block in which this id use occurs, recording the id as // an input id if the block is outside the region, with some // exceptions detailed below. - auto use_block = context->get_instr_block(use); + auto use_block = ir_context->get_instr_block(use); if (!use_block) { // There might be no containing block, e.g. if the use is in a @@ -467,7 +472,7 @@ std::vector TransformationOutlineFunction::GetRegionInputIds( } std::vector TransformationOutlineFunction::GetRegionOutputIds( - opt::IRContext* context, const std::set& region_set, + opt::IRContext* ir_context, const std::set& region_set, opt::BasicBlock* region_exit_block) { std::vector result; @@ -479,15 +484,15 @@ std::vector TransformationOutlineFunction::GetRegionOutputIds( } // Consider each use of each instruction defined in the block. for (auto& inst : block) { - context->get_def_use_mgr()->WhileEachUse( + ir_context->get_def_use_mgr()->WhileEachUse( &inst, - [®ion_set, context, &inst, region_exit_block, &result]( + [®ion_set, ir_context, &inst, region_exit_block, &result]( opt::Instruction* use, uint32_t /*unused*/) -> bool { // Find the block in which this id use occurs, recording the id as // an output id if the block is outside the region, with some // exceptions detailed below. - auto use_block = context->get_instr_block(use); + auto use_block = ir_context->get_instr_block(use); if (!use_block) { // There might be no containing block, e.g. if the use is in a @@ -513,12 +518,13 @@ std::vector TransformationOutlineFunction::GetRegionOutputIds( } std::set TransformationOutlineFunction::GetRegionBlocks( - opt::IRContext* context, opt::BasicBlock* entry_block, + opt::IRContext* ir_context, opt::BasicBlock* entry_block, opt::BasicBlock* exit_block) { auto enclosing_function = entry_block->GetParent(); - auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); + auto dominator_analysis = + ir_context->GetDominatorAnalysis(enclosing_function); auto postdominator_analysis = - context->GetPostDominatorAnalysis(enclosing_function); + ir_context->GetPostDominatorAnalysis(enclosing_function); std::set result; for (auto& block : *enclosing_function) { @@ -535,7 +541,8 @@ TransformationOutlineFunction::PrepareFunctionPrototype( const std::vector& region_input_ids, const std::vector& region_output_ids, const std::map& input_id_to_fresh_id_map, - opt::IRContext* context, FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { uint32_t return_type_id = 0; uint32_t function_type_id = 0; @@ -547,14 +554,14 @@ TransformationOutlineFunction::PrepareFunctionPrototype( if (region_output_ids.empty()) { std::vector return_and_parameter_types; opt::analysis::Void void_type; - return_type_id = context->get_type_mgr()->GetId(&void_type); + return_type_id = ir_context->get_type_mgr()->GetId(&void_type); return_and_parameter_types.push_back(return_type_id); for (auto id : region_input_ids) { return_and_parameter_types.push_back( - context->get_def_use_mgr()->GetDef(id)->type_id()); + ir_context->get_def_use_mgr()->GetDef(id)->type_id()); } function_type_id = - fuzzerutil::FindFunctionType(context, return_and_parameter_types); + fuzzerutil::FindFunctionType(ir_context, return_and_parameter_types); } // If no existing function type was found, we need to create one. @@ -568,12 +575,12 @@ TransformationOutlineFunction::PrepareFunctionPrototype( opt::Instruction::OperandList struct_member_types; for (uint32_t output_id : region_output_ids) { auto output_id_type = - context->get_def_use_mgr()->GetDef(output_id)->type_id(); + ir_context->get_def_use_mgr()->GetDef(output_id)->type_id(); struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}}); } // Add a new struct type to the module. - context->module()->AddType(MakeUnique( - context, SpvOpTypeStruct, 0, + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeStruct, 0, message_.new_function_struct_return_type_id(), std::move(struct_member_types))); // The return type for the function is the newly-created struct. @@ -589,12 +596,12 @@ TransformationOutlineFunction::PrepareFunctionPrototype( for (auto id : region_input_ids) { function_type_operands.push_back( {SPV_OPERAND_TYPE_ID, - {context->get_def_use_mgr()->GetDef(id)->type_id()}}); + {ir_context->get_def_use_mgr()->GetDef(id)->type_id()}}); } // Add a new function type to the module, and record that this is the type // id for the new function. - context->module()->AddType(MakeUnique( - context, SpvOpTypeFunction, 0, message_.new_function_type_id(), + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeFunction, 0, message_.new_function_type_id(), function_type_operands)); function_type_id = message_.new_function_type_id(); } @@ -603,7 +610,7 @@ TransformationOutlineFunction::PrepareFunctionPrototype( // and the return type and function type prepared above. std::unique_ptr outlined_function = MakeUnique(MakeUnique( - context, SpvOpFunction, return_type_id, message_.new_function_id(), + ir_context, SpvOpFunction, return_type_id, message_.new_function_id(), opt::Instruction::OperandList( {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvFunctionControlMaskNone}}, @@ -614,14 +621,15 @@ TransformationOutlineFunction::PrepareFunctionPrototype( // provided in |input_id_to_fresh_id_map|. for (auto id : region_input_ids) { outlined_function->AddParameter(MakeUnique( - context, SpvOpFunctionParameter, - context->get_def_use_mgr()->GetDef(id)->type_id(), + ir_context, SpvOpFunctionParameter, + ir_context->get_def_use_mgr()->GetDef(id)->type_id(), input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList())); // If the input id is an irrelevant-valued variable, the same should be true // of the corresponding parameter. - if (fact_manager->PointeeValueIsIrrelevant(id)) { - fact_manager->AddFactValueOfPointeeIsIrrelevant( - input_id_to_fresh_id_map.at(id)); + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + id)) { + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(input_id_to_fresh_id_map.at(id)); } } @@ -629,31 +637,32 @@ TransformationOutlineFunction::PrepareFunctionPrototype( } void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds( - opt::IRContext* context, + opt::IRContext* ir_context, const std::map& input_id_to_fresh_id_map, const std::map& output_id_to_fresh_id_map) const { // Enlarge the module's id bound as needed to accommodate the various fresh // ids associated with the transformation. fuzzerutil::UpdateModuleIdBound( - context, message_.new_function_struct_return_type_id()); - fuzzerutil::UpdateModuleIdBound(context, message_.new_function_type_id()); - fuzzerutil::UpdateModuleIdBound(context, message_.new_function_id()); - fuzzerutil::UpdateModuleIdBound(context, + ir_context, message_.new_function_struct_return_type_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_type_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_region_entry_block()); - fuzzerutil::UpdateModuleIdBound(context, message_.new_caller_result_id()); - fuzzerutil::UpdateModuleIdBound(context, message_.new_callee_result_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_caller_result_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_callee_result_id()); for (auto& entry : input_id_to_fresh_id_map) { - fuzzerutil::UpdateModuleIdBound(context, entry.second); + fuzzerutil::UpdateModuleIdBound(ir_context, entry.second); } for (auto& entry : output_id_to_fresh_id_map) { - fuzzerutil::UpdateModuleIdBound(context, entry.second); + fuzzerutil::UpdateModuleIdBound(ir_context, entry.second); } } void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion( - opt::IRContext* context, const opt::BasicBlock& original_region_exit_block, + opt::IRContext* ir_context, + const opt::BasicBlock& original_region_exit_block, const std::set& region_blocks, const std::vector& region_input_ids, const std::vector& region_output_ids, @@ -664,11 +673,11 @@ void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion( // This is done by considering each region input id in turn. for (uint32_t id : region_input_ids) { // We then consider each use of the input id. - context->get_def_use_mgr()->ForEachUse( - id, [context, id, &input_id_to_fresh_id_map, region_blocks]( + ir_context->get_def_use_mgr()->ForEachUse( + id, [ir_context, id, &input_id_to_fresh_id_map, region_blocks]( opt::Instruction* use, uint32_t operand_index) { // Find the block in which this use of the input id occurs. - opt::BasicBlock* use_block = context->get_instr_block(use); + opt::BasicBlock* use_block = ir_context->get_instr_block(use); // We want to rewrite the use id if its block occurs in the outlined // region. if (region_blocks.count(use_block) != 0) { @@ -684,12 +693,12 @@ void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion( // This is done by considering each region output id in turn. for (uint32_t id : region_output_ids) { // First consider each use of the output id and update the relevant uses. - context->get_def_use_mgr()->ForEachUse( - id, - [context, &original_region_exit_block, id, &output_id_to_fresh_id_map, - region_blocks](opt::Instruction* use, uint32_t operand_index) { + ir_context->get_def_use_mgr()->ForEachUse( + id, [ir_context, &original_region_exit_block, id, + &output_id_to_fresh_id_map, + region_blocks](opt::Instruction* use, uint32_t operand_index) { // Find the block in which this use of the output id occurs. - auto use_block = context->get_instr_block(use); + auto use_block = ir_context->get_instr_block(use); // We want to rewrite the use id if its block occurs in the outlined // region, with one exception: the terminator of the exit block of // the region is going to remain in the original function, so if the @@ -710,7 +719,7 @@ void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion( // defines the corresponding fresh id. We do this after changing all the // uses so that the definition of the original id is still registered when // we analyse its uses. - context->get_def_use_mgr()->GetDef(id)->SetResultId( + ir_context->get_def_use_mgr()->GetDef(id)->SetResultId( output_id_to_fresh_id_map.at(id)); } } @@ -721,8 +730,8 @@ void TransformationOutlineFunction::PopulateOutlinedFunction( const std::set& region_blocks, const std::vector& region_output_ids, const std::map& output_id_to_fresh_id_map, - opt::IRContext* context, opt::Function* outlined_function, - FactManager* fact_manager) const { + opt::IRContext* ir_context, opt::Function* outlined_function, + TransformationContext* transformation_context) const { // When we create the exit block for the outlined region, we use this pointer // to track of it so that we can manipulate it later. opt::BasicBlock* outlined_region_exit_block = nullptr; @@ -732,14 +741,16 @@ void TransformationOutlineFunction::PopulateOutlinedFunction( // |message_.new_function_region_entry_block| as its id. std::unique_ptr outlined_region_entry_block = MakeUnique(MakeUnique( - context, SpvOpLabel, 0, message_.new_function_region_entry_block(), + ir_context, SpvOpLabel, 0, message_.new_function_region_entry_block(), opt::Instruction::OperandList())); outlined_region_entry_block->SetParent(outlined_function); // If the original region's entry block was dead, the outlined region's entry // block is also dead. - if (fact_manager->BlockIsDead(original_region_entry_block.id())) { - fact_manager->AddFactBlockIsDead(outlined_region_entry_block->id()); + if (transformation_context->GetFactManager()->BlockIsDead( + original_region_entry_block.id())) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + outlined_region_entry_block->id()); } if (&original_region_entry_block == &original_region_exit_block) { @@ -748,7 +759,7 @@ void TransformationOutlineFunction::PopulateOutlinedFunction( for (auto& inst : original_region_entry_block) { outlined_region_entry_block->AddInstruction( - std::unique_ptr(inst.Clone(context))); + std::unique_ptr(inst.Clone(ir_context))); } outlined_function->AddBasicBlock(std::move(outlined_region_entry_block)); @@ -767,7 +778,7 @@ void TransformationOutlineFunction::PopulateOutlinedFunction( } // Clone the block so that it can be added to the new function. auto cloned_block = - std::unique_ptr(block_it->Clone(context)); + std::unique_ptr(block_it->Clone(ir_context)); // If this is the region's exit block, then the cloned block is the outlined // region's exit block. @@ -823,7 +834,7 @@ void TransformationOutlineFunction::PopulateOutlinedFunction( // The case where there are no region output ids is simple: we just add // OpReturn. outlined_region_exit_block->AddInstruction(MakeUnique( - context, SpvOpReturn, 0, 0, opt::Instruction::OperandList())); + ir_context, SpvOpReturn, 0, 0, opt::Instruction::OperandList())); } else { // In the case where there are output ids, we add an OpCompositeConstruct // instruction to pack all the output values into a struct, and then an @@ -834,21 +845,21 @@ void TransformationOutlineFunction::PopulateOutlinedFunction( {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}}); } outlined_region_exit_block->AddInstruction(MakeUnique( - context, SpvOpCompositeConstruct, + ir_context, SpvOpCompositeConstruct, message_.new_function_struct_return_type_id(), message_.new_callee_result_id(), struct_member_operands)); outlined_region_exit_block->AddInstruction(MakeUnique( - context, SpvOpReturnValue, 0, 0, + ir_context, SpvOpReturnValue, 0, 0, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}}))); } outlined_function->SetFunctionEnd(MakeUnique( - context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList())); + ir_context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList())); } void TransformationOutlineFunction::ShrinkOriginalRegion( - opt::IRContext* context, std::set& region_blocks, + opt::IRContext* ir_context, std::set& region_blocks, const std::vector& region_input_ids, const std::vector& region_output_ids, const std::map& output_id_to_type_id, @@ -912,7 +923,7 @@ void TransformationOutlineFunction::ShrinkOriginalRegion( } original_region_entry_block->AddInstruction(MakeUnique( - context, SpvOpFunctionCall, return_type_id, + ir_context, SpvOpFunctionCall, return_type_id, message_.new_caller_result_id(), function_call_operands)); // If there are output ids, the function call will return a struct. For each @@ -921,7 +932,7 @@ void TransformationOutlineFunction::ShrinkOriginalRegion( for (uint32_t index = 0; index < region_output_ids.size(); ++index) { uint32_t output_id = region_output_ids[index]; original_region_entry_block->AddInstruction(MakeUnique( - context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id), + ir_context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id), output_id, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}}, diff --git a/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h index 5711790620..ba439c8428 100644 --- a/source/fuzz/transformation_outline_function.h +++ b/source/fuzz/transformation_outline_function.h @@ -19,9 +19,9 @@ #include #include -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -58,8 +58,9 @@ class TransformationOutlineFunction : public Transformation { // defined outside the region but used in the region // - |message_.output_id_to_fresh_id| must contain an entry for every id // defined in the region but used outside the region - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // - A new function with id |message_.new_function_id| is added to the module. // - If the region generates output ids, the return type of this function is @@ -95,14 +96,15 @@ class TransformationOutlineFunction : public Transformation { // |message_.new_function_struct_return_type| comprised of all the fresh // output ids (unless the return type is void, in which case no value is // returned. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; // Returns the set of blocks dominated by |entry_block| and post-dominated // by |exit_block|. static std::set GetRegionBlocks( - opt::IRContext* context, opt::BasicBlock* entry_block, + opt::IRContext* ir_context, opt::BasicBlock* entry_block, opt::BasicBlock* exit_block); // Yields ids that are used in |region_set| and that are either parameters @@ -114,7 +116,7 @@ class TransformationOutlineFunction : public Transformation { // - id uses in OpPhi instructions in |region_entry_block| are ignored // - id uses in the terminator instruction of |region_exit_block| are ignored static std::vector GetRegionInputIds( - opt::IRContext* context, const std::set& region_set, + opt::IRContext* ir_context, const std::set& region_set, opt::BasicBlock* region_exit_block); // Yields all ids that are defined in |region_set| and used outside @@ -124,14 +126,14 @@ class TransformationOutlineFunction : public Transformation { // - ids defined in the region and used in the terminator of // |region_exit_block| count as output ids static std::vector GetRegionOutputIds( - opt::IRContext* context, const std::set& region_set, + opt::IRContext* ir_context, const std::set& region_set, opt::BasicBlock* region_exit_block); private: // Ensures that the module's id bound is at least the maximum of any fresh id // associated with the transformation. void UpdateModuleIdBoundForFreshIds( - opt::IRContext* context, + opt::IRContext* ir_context, const std::map& input_id_to_fresh_id_map, const std::map& output_id_to_fresh_id_map) const; @@ -142,7 +144,7 @@ class TransformationOutlineFunction : public Transformation { // modified, and |original_region_exit_block| allows for some special cases // where ids should not be remapped. void RemapInputAndOutputIdsInRegion( - opt::IRContext* context, + opt::IRContext* ir_context, const opt::BasicBlock& original_region_exit_block, const std::set& region_blocks, const std::vector& region_input_ids, @@ -160,12 +162,14 @@ class TransformationOutlineFunction : public Transformation { // are already present). // // Facts about the function containing the outlined region that are relevant - // to the new function are propagated via |fact_manager|. + // to the new function are propagated via the vact manager in + // |transformation_context|. std::unique_ptr PrepareFunctionPrototype( const std::vector& region_input_ids, const std::vector& region_output_ids, const std::map& input_id_to_fresh_id_map, - opt::IRContext* context, FactManager* fact_manager) const; + opt::IRContext* ir_context, + TransformationContext* transformation_context) const; // Creates the body of the outlined function by cloning blocks from the // original region, given by |region_blocks|, adapting the cloned version @@ -174,17 +178,17 @@ class TransformationOutlineFunction : public Transformation { // clone. Parameters |region_output_ids| and |output_id_to_fresh_id_map| are // used to determine what the function should return. // - // The |fact_manager| argument allow facts about blocks being outlined, e.g. - // whether they are dead blocks, to be asserted about blocks that get created - // during outlining. + // The |transformation_context| argument allow facts about blocks being + // outlined, e.g. whether they are dead blocks, to be asserted about blocks + // that get created during outlining. void PopulateOutlinedFunction( const opt::BasicBlock& original_region_entry_block, const opt::BasicBlock& original_region_exit_block, const std::set& region_blocks, const std::vector& region_output_ids, const std::map& output_id_to_fresh_id_map, - opt::IRContext* context, opt::Function* outlined_function, - FactManager* fact_manager) const; + opt::IRContext* ir_context, opt::Function* outlined_function, + TransformationContext* transformation_context) const; // Shrinks the outlined region, given by |region_blocks|, down to the single // block |original_region_entry_block|. This block is itself shrunk to just @@ -203,7 +207,7 @@ class TransformationOutlineFunction : public Transformation { // function is called, this information cannot be gotten from the def-use // manager. void ShrinkOriginalRegion( - opt::IRContext* context, std::set& region_blocks, + opt::IRContext* ir_context, std::set& region_blocks, const std::vector& region_input_ids, const std::vector& region_output_ids, const std::map& output_id_to_type_id, diff --git a/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp index 2141533551..0f1220e293 100644 --- a/source/fuzz/transformation_permute_function_parameters.cpp +++ b/source/fuzz/transformation_permute_function_parameters.cpp @@ -40,17 +40,17 @@ TransformationPermuteFunctionParameters:: } bool TransformationPermuteFunctionParameters::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // Check that function exists const auto* function = - fuzzerutil::FindFunction(context, message_.function_id()); + fuzzerutil::FindFunction(ir_context, message_.function_id()); if (!function || function->DefInst().opcode() != SpvOpFunction || - fuzzerutil::FunctionIsEntryPoint(context, function->result_id())) { + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { return false; } // Check that permutation has valid indices - const auto* function_type = fuzzerutil::GetFunctionType(context, function); + const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function); assert(function_type && "Function type is null"); const auto& permutation = message_.permutation(); @@ -83,7 +83,7 @@ bool TransformationPermuteFunctionParameters::IsApplicable( // - Has the same result type as the old one // - Order of arguments is permuted auto new_type_id = message_.new_type_id(); - const auto* new_type = context->get_def_use_mgr()->GetDef(new_type_id); + const auto* new_type = ir_context->get_def_use_mgr()->GetDef(new_type_id); if (!new_type || new_type->opcode() != SpvOpTypeFunction || new_type->NumInOperands() != function_type->NumInOperands()) { @@ -109,14 +109,14 @@ bool TransformationPermuteFunctionParameters::IsApplicable( } void TransformationPermuteFunctionParameters::Apply( - opt::IRContext* context, FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { // Retrieve all data from the message uint32_t function_id = message_.function_id(); uint32_t new_type_id = message_.new_type_id(); const auto& permutation = message_.permutation(); // Find the function that will be transformed - auto* function = fuzzerutil::FindFunction(context, function_id); + auto* function = fuzzerutil::FindFunction(ir_context, function_id); assert(function && "Can't find the function"); // Change function's type @@ -149,7 +149,7 @@ void TransformationPermuteFunctionParameters::Apply( }); // Fix all OpFunctionCall instructions - context->get_def_use_mgr()->ForEachUser( + ir_context->get_def_use_mgr()->ForEachUser( &function->DefInst(), [function_id, &permutation](opt::Instruction* call) { if (call->opcode() != SpvOpFunctionCall || @@ -170,7 +170,8 @@ void TransformationPermuteFunctionParameters::Apply( }); // Make sure our changes are analyzed - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage() diff --git a/source/fuzz/transformation_permute_function_parameters.h b/source/fuzz/transformation_permute_function_parameters.h index c67a735697..994e4c24bc 100644 --- a/source/fuzz/transformation_permute_function_parameters.h +++ b/source/fuzz/transformation_permute_function_parameters.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ #define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -40,14 +40,16 @@ class TransformationPermuteFunctionParameters : public Transformation { // - function's arguments are permuted according to |permutation| vector // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments // to the function - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // - OpFunction instruction with |result_id == function_id| is changed. // Its arguments are permuted according to the |permutation| vector // - Changed function gets a new type specified by |type_id| // - Calls to the function are adjusted accordingly - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp index 72d9b228d9..d6f17fc376 100644 --- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp +++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp @@ -128,15 +128,15 @@ TransformationReplaceBooleanConstantWithConstantBinary:: } bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The id for the binary result must be fresh - if (!fuzzerutil::IsFreshId(context, + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id_for_binary_operation())) { return false; } // The used id must be for a boolean constant - auto boolean_constant = context->get_def_use_mgr()->GetDef( + auto boolean_constant = ir_context->get_def_use_mgr()->GetDef( message_.id_use_descriptor().id_of_interest()); if (!boolean_constant) { return false; @@ -148,7 +148,7 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( // The left-hand-side id must correspond to a constant instruction. auto lhs_constant_inst = - context->get_def_use_mgr()->GetDef(message_.lhs_id()); + ir_context->get_def_use_mgr()->GetDef(message_.lhs_id()); if (!lhs_constant_inst) { return false; } @@ -158,7 +158,7 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( // The right-hand-side id must correspond to a constant instruction. auto rhs_constant_inst = - context->get_def_use_mgr()->GetDef(message_.rhs_id()); + ir_context->get_def_use_mgr()->GetDef(message_.rhs_id()); if (!rhs_constant_inst) { return false; } @@ -173,9 +173,9 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( // The expression 'LHS opcode RHS' must evaluate to the boolean constant. auto lhs_constant = - context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id()); + ir_context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id()); auto rhs_constant = - context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id()); + ir_context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id()); bool expected_result = (boolean_constant->opcode() == SpvOpConstantTrue); const auto binary_opcode = static_cast(message_.opcode()); @@ -238,7 +238,7 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( // The id use descriptor must identify some instruction auto instruction = - FindInstructionContainingUse(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); if (instruction == nullptr) { return false; } @@ -262,24 +262,25 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( } void TransformationReplaceBooleanConstantWithConstantBinary::Apply( - opt::IRContext* context, FactManager* fact_manager) const { - ApplyWithResult(context, fact_manager); + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ApplyWithResult(ir_context, transformation_context); } opt::Instruction* TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult( - opt::IRContext* context, FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::analysis::Bool bool_type; opt::Instruction::OperandList operands = { {SPV_OPERAND_TYPE_ID, {message_.lhs_id()}}, {SPV_OPERAND_TYPE_ID, {message_.rhs_id()}}}; auto binary_instruction = MakeUnique( - context, static_cast(message_.opcode()), - context->get_type_mgr()->GetId(&bool_type), + ir_context, static_cast(message_.opcode()), + ir_context->get_type_mgr()->GetId(&bool_type), message_.fresh_id_for_binary_operation(), operands); opt::Instruction* result = binary_instruction.get(); auto instruction_containing_constant_use = - FindInstructionContainingUse(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), ir_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 @@ -298,9 +299,10 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult( instruction_containing_constant_use->SetInOperand( message_.id_use_descriptor().in_operand_index(), {message_.fresh_id_for_binary_operation()}); - fuzzerutil::UpdateModuleIdBound(context, + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_binary_operation()); - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); return result; } diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h index f74cd8d178..3abb4854b1 100644 --- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h +++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ #define SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -49,20 +49,23 @@ class TransformationReplaceBooleanConstantWithConstantBinary // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): consider // replacing a boolean in an OpPhi by adding a binary operator instruction // to the parent block for the OpPhi. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // A new instruction is added before the boolean constant usage that computes // the result of applying |message_.opcode| to |message_.lhs_id| and // |message_.rhs_id| is added, with result id // |message_.fresh_id_for_binary_operation|. The boolean constant usage is // replaced with this result id. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; // The same as Apply, except that the newly-added binary instruction is // returned. - opt::Instruction* ApplyWithResult(opt::IRContext* context, - FactManager* fact_manager) const; + opt::Instruction* ApplyWithResult( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp index 8e0e4e5a64..a8f94954a7 100644 --- a/source/fuzz/transformation_replace_constant_with_uniform.cpp +++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp @@ -39,12 +39,12 @@ TransformationReplaceConstantWithUniform:: std::unique_ptr TransformationReplaceConstantWithUniform::MakeAccessChainInstruction( - spvtools::opt::IRContext* context, uint32_t constant_type_id) const { + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const { // The input operands for the access chain. opt::Instruction::OperandList operands_for_access_chain; opt::Instruction* uniform_variable = - FindUniformVariable(message_.uniform_descriptor(), context, false); + FindUniformVariable(message_.uniform_descriptor(), ir_context, false); // The first input operand is the id of the uniform variable. operands_for_access_chain.push_back( @@ -56,42 +56,43 @@ TransformationReplaceConstantWithUniform::MakeAccessChainInstruction( // instruction ids as operands. opt::analysis::Integer int_type(32, true); auto registered_int_type = - context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); - auto int_type_id = context->get_type_mgr()->GetId(&int_type); + ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type); for (auto index : message_.uniform_descriptor().index()) { opt::analysis::IntConstant int_constant(registered_int_type, {index}); - auto constant_id = context->get_constant_mgr()->FindDeclaredConstant( + auto constant_id = ir_context->get_constant_mgr()->FindDeclaredConstant( &int_constant, int_type_id); operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}}); } // The type id for the access chain is a uniform pointer with base type // matching the given constant id type. - auto type_and_pointer_type = context->get_type_mgr()->GetTypeAndPointerType( - constant_type_id, SpvStorageClassUniform); + auto type_and_pointer_type = + ir_context->get_type_mgr()->GetTypeAndPointerType(constant_type_id, + SpvStorageClassUniform); assert(type_and_pointer_type.first != nullptr); assert(type_and_pointer_type.second != nullptr); auto pointer_to_uniform_constant_type_id = - context->get_type_mgr()->GetId(type_and_pointer_type.second.get()); + ir_context->get_type_mgr()->GetId(type_and_pointer_type.second.get()); return MakeUnique( - context, SpvOpAccessChain, pointer_to_uniform_constant_type_id, + ir_context, SpvOpAccessChain, pointer_to_uniform_constant_type_id, message_.fresh_id_for_access_chain(), operands_for_access_chain); } std::unique_ptr TransformationReplaceConstantWithUniform::MakeLoadInstruction( - spvtools::opt::IRContext* context, uint32_t constant_type_id) const { + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const { opt::Instruction::OperandList operands_for_load = { {SPV_OPERAND_TYPE_ID, {message_.fresh_id_for_access_chain()}}}; - return MakeUnique(context, SpvOpLoad, constant_type_id, + return MakeUnique(ir_context, SpvOpLoad, constant_type_id, message_.fresh_id_for_load(), operands_for_load); } bool TransformationReplaceConstantWithUniform::IsApplicable( - spvtools::opt::IRContext* context, - const spvtools::fuzz::FactManager& fact_manager) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // The following is really an invariant of the transformation rather than // merely a requirement of the precondition. We check it here since we cannot // check it in the message_ constructor. @@ -99,16 +100,17 @@ bool TransformationReplaceConstantWithUniform::IsApplicable( "Fresh ids for access chain and load result cannot be the same."); // The ids for the access chain and load instructions must both be fresh. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_access_chain())) { + if (!fuzzerutil::IsFreshId(ir_context, + message_.fresh_id_for_access_chain())) { return false; } - if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_load())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id_for_load())) { return false; } // The id specified in the id use descriptor must be that of a declared scalar // constant. - auto declared_constant = context->get_constant_mgr()->FindDeclaredConstant( + auto declared_constant = ir_context->get_constant_mgr()->FindDeclaredConstant( message_.id_use_descriptor().id_of_interest()); if (!declared_constant) { return false; @@ -120,13 +122,13 @@ bool TransformationReplaceConstantWithUniform::IsApplicable( // The fact manager needs to believe that the uniform data element described // by the uniform buffer element descriptor will hold a scalar value. auto constant_id_associated_with_uniform = - fact_manager.GetConstantFromUniformDescriptor( - context, message_.uniform_descriptor()); + transformation_context.GetFactManager()->GetConstantFromUniformDescriptor( + ir_context, message_.uniform_descriptor()); if (!constant_id_associated_with_uniform) { return false; } auto constant_associated_with_uniform = - context->get_constant_mgr()->FindDeclaredConstant( + ir_context->get_constant_mgr()->FindDeclaredConstant( constant_id_associated_with_uniform); assert(constant_associated_with_uniform && "The constant should be present in the module."); @@ -149,7 +151,7 @@ bool TransformationReplaceConstantWithUniform::IsApplicable( // The id use descriptor must identify some instruction with respect to the // module. auto instruction_using_constant = - FindInstructionContainingUse(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); if (!instruction_using_constant) { return false; } @@ -165,23 +167,23 @@ bool TransformationReplaceConstantWithUniform::IsApplicable( // replace with a uniform. opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(), SpvStorageClassUniform); - if (!context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) { + if (!ir_context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) { return false; } // In order to index into the uniform, the module has got to contain the int32 // type, plus an OpConstant for each of the indices of interest. opt::analysis::Integer int_type(32, true); - if (!context->get_type_mgr()->GetId(&int_type)) { + if (!ir_context->get_type_mgr()->GetId(&int_type)) { return false; } auto registered_int_type = - context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); - auto int_type_id = context->get_type_mgr()->GetId(&int_type); + ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type); for (auto index : message_.uniform_descriptor().index()) { opt::analysis::IntConstant int_constant(registered_int_type, {index}); - if (!context->get_constant_mgr()->FindDeclaredConstant(&int_constant, - int_type_id)) { + if (!ir_context->get_constant_mgr()->FindDeclaredConstant(&int_constant, + int_type_id)) { return false; } } @@ -190,11 +192,11 @@ bool TransformationReplaceConstantWithUniform::IsApplicable( } void TransformationReplaceConstantWithUniform::Apply( - spvtools::opt::IRContext* context, - spvtools::fuzz::FactManager* /*unused*/) const { + spvtools::opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { // Get the instruction that contains the id use we wish to replace. auto instruction_containing_constant_use = - FindInstructionContainingUse(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); assert(instruction_containing_constant_use && "Precondition requires that the id use can be found."); assert(instruction_containing_constant_use->GetSingleWordInOperand( @@ -204,17 +206,17 @@ void TransformationReplaceConstantWithUniform::Apply( // The id of the type for the constant whose use we wish to replace. auto constant_type_id = - context->get_def_use_mgr() + ir_context->get_def_use_mgr() ->GetDef(message_.id_use_descriptor().id_of_interest()) ->type_id(); // Add an access chain instruction to target the uniform element. instruction_containing_constant_use->InsertBefore( - MakeAccessChainInstruction(context, constant_type_id)); + MakeAccessChainInstruction(ir_context, constant_type_id)); // Add a load from this access chain. instruction_containing_constant_use->InsertBefore( - MakeLoadInstruction(context, constant_type_id)); + MakeLoadInstruction(ir_context, constant_type_id)); // Adjust the instruction containing the usage of the constant so that this // usage refers instead to the result of the load. @@ -223,11 +225,12 @@ void TransformationReplaceConstantWithUniform::Apply( {message_.fresh_id_for_load()}); // Update the module id bound to reflect the new instructions. - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id_for_load()); - fuzzerutil::UpdateModuleIdBound(context, + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_access_chain()); - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage() diff --git a/source/fuzz/transformation_replace_constant_with_uniform.h b/source/fuzz/transformation_replace_constant_with_uniform.h index ed354b1dbc..b72407c850 100644 --- a/source/fuzz/transformation_replace_constant_with_uniform.h +++ b/source/fuzz/transformation_replace_constant_with_uniform.h @@ -58,8 +58,9 @@ class TransformationReplaceConstantWithUniform : public Transformation { // - According to the fact manager, the uniform data element specified by // |message_.uniform_descriptor| holds a value with the same type and // value as %C - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // - Introduces two new instructions: // - An access chain targeting the uniform data element specified by @@ -68,7 +69,8 @@ class TransformationReplaceConstantWithUniform : public Transformation { // - A load from this access chain, with id |message_.fresh_id_for_load| // - Replaces the id use specified by |message_.id_use_descriptor| with // |message_.fresh_id_for_load| - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -76,11 +78,11 @@ class TransformationReplaceConstantWithUniform : public Transformation { // Helper method to create an access chain for the uniform element associated // with the transformation. std::unique_ptr MakeAccessChainInstruction( - spvtools::opt::IRContext* context, uint32_t constant_type_id) const; + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const; // Helper to create a load instruction. std::unique_ptr MakeLoadInstruction( - spvtools::opt::IRContext* context, uint32_t constant_type_id) const; + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const; protobufs::TransformationReplaceConstantWithUniform message_; }; diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp index 88c977a240..87194c8eb7 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -37,28 +37,29 @@ TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( } bool TransformationReplaceIdWithSynonym::IsApplicable( - spvtools::opt::IRContext* context, - const spvtools::fuzz::FactManager& fact_manager) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { auto id_of_interest = message_.id_use_descriptor().id_of_interest(); // Does the fact manager know about the synonym? auto data_descriptor_for_synonymous_id = MakeDataDescriptor(message_.synonymous_id(), {}); - if (!fact_manager.IsSynonymous(MakeDataDescriptor(id_of_interest, {}), - data_descriptor_for_synonymous_id, context)) { + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(id_of_interest, {}), + data_descriptor_for_synonymous_id, ir_context)) { return false; } // Does the id use descriptor in the transformation identify an instruction? auto use_instruction = - FindInstructionContainingUse(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); if (!use_instruction) { return false; } // Is the use suitable for being replaced in principle? if (!UseCanBeReplacedWithSynonym( - context, use_instruction, + ir_context, use_instruction, message_.id_use_descriptor().in_operand_index())) { return false; } @@ -66,19 +67,21 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( // The transformation is applicable if the synonymous id is available at the // use point. return fuzzerutil::IdIsAvailableAtUse( - context, use_instruction, message_.id_use_descriptor().in_operand_index(), + ir_context, use_instruction, + message_.id_use_descriptor().in_operand_index(), message_.synonymous_id()); } void TransformationReplaceIdWithSynonym::Apply( - spvtools::opt::IRContext* context, - spvtools::fuzz::FactManager* /*unused*/) const { + spvtools::opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { auto instruction_to_change = - FindInstructionContainingUse(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); instruction_to_change->SetInOperand( message_.id_use_descriptor().in_operand_index(), {message_.synonymous_id()}); - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() @@ -89,7 +92,7 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() } bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym( - opt::IRContext* context, opt::Instruction* use_instruction, + opt::IRContext* ir_context, opt::Instruction* use_instruction, uint32_t use_in_operand_index) { if (use_instruction->opcode() == SpvOpAccessChain && use_in_operand_index > 0) { @@ -98,10 +101,10 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym( // synonym, as the use needs to be an OpConstant. // Get the top-level composite type that is being accessed. - auto object_being_accessed = context->get_def_use_mgr()->GetDef( + auto object_being_accessed = ir_context->get_def_use_mgr()->GetDef( use_instruction->GetSingleWordInOperand(0)); auto pointer_type = - context->get_type_mgr()->GetType(object_being_accessed->type_id()); + ir_context->get_type_mgr()->GetType(object_being_accessed->type_id()); assert(pointer_type->AsPointer()); auto composite_type_being_accessed = pointer_type->AsPointer()->pointee_type(); @@ -124,7 +127,7 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym( composite_type_being_accessed->AsArray()->element_type(); } else { assert(composite_type_being_accessed->AsStruct()); - auto constant_index_instruction = context->get_def_use_mgr()->GetDef( + auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef( use_instruction->GetSingleWordInOperand(index_in_operand)); assert(constant_index_instruction->opcode() == SpvOpConstant); uint32_t member_index = @@ -149,16 +152,16 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym( // type. // Get the definition of the function being called. - auto function = context->get_def_use_mgr()->GetDef( + auto function = ir_context->get_def_use_mgr()->GetDef( use_instruction->GetSingleWordInOperand(0)); // From the function definition, get the function type. - auto function_type = - context->get_def_use_mgr()->GetDef(function->GetSingleWordInOperand(1)); + auto function_type = ir_context->get_def_use_mgr()->GetDef( + function->GetSingleWordInOperand(1)); // OpTypeFunction's 0-th input operand is the function return type, and the // function argument types follow. Because the arguments to OpFunctionCall // start from input operand 1, we can use |use_in_operand_index| to get the // type associated with this function argument. - auto parameter_type = context->get_type_mgr()->GetType( + auto parameter_type = ir_context->get_type_mgr()->GetType( function_type->GetSingleWordInOperand(use_in_operand_index)); if (parameter_type->AsPointer()) { return false; diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h index 48132c1e0f..a5a9dfda16 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.h +++ b/source/fuzz/transformation_replace_id_with_synonym.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ #define SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -42,12 +42,14 @@ class TransformationReplaceIdWithSynonym : public Transformation { // - The id must not be a pointer argument to a function call (because the // synonym might not be a memory object declaration). // - |fresh_id_for_temporary| must be 0. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Replaces the use identified by |message_.id_use_descriptor| with the // synonymous id identified by |message_.synonymous_id|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -58,7 +60,7 @@ class TransformationReplaceIdWithSynonym : public Transformation { // indices must be constants, so it is dangerous to replace them. // - the id use is not a pointer function call argument, on which there are // restrictions that make replacement problematic. - static bool UseCanBeReplacedWithSynonym(opt::IRContext* context, + static bool UseCanBeReplacedWithSynonym(opt::IRContext* ir_context, opt::Instruction* use_instruction, uint32_t use_in_operand_index); diff --git a/source/fuzz/transformation_set_function_control.cpp b/source/fuzz/transformation_set_function_control.cpp index d2b61f198a..d01e74347e 100644 --- a/source/fuzz/transformation_set_function_control.cpp +++ b/source/fuzz/transformation_set_function_control.cpp @@ -28,9 +28,9 @@ TransformationSetFunctionControl::TransformationSetFunctionControl( } bool TransformationSetFunctionControl::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { opt::Instruction* function_def_instruction = - FindFunctionDefInstruction(context); + FindFunctionDefInstruction(ir_context); if (!function_def_instruction) { // The given function id does not correspond to any function. return false; @@ -69,10 +69,10 @@ bool TransformationSetFunctionControl::IsApplicable( return true; } -void TransformationSetFunctionControl::Apply(opt::IRContext* context, - FactManager* /*unused*/) const { +void TransformationSetFunctionControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction* function_def_instruction = - FindFunctionDefInstruction(context); + FindFunctionDefInstruction(ir_context); function_def_instruction->SetInOperand(0, {message_.function_control()}); } @@ -83,11 +83,11 @@ protobufs::Transformation TransformationSetFunctionControl::ToMessage() const { } opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction( - opt::IRContext* context) const { + opt::IRContext* ir_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()) { + for (auto& function : *ir_context->module()) { if (function.DefInst().result_id() == message_.function_id()) { return &function.DefInst(); } diff --git a/source/fuzz/transformation_set_function_control.h b/source/fuzz/transformation_set_function_control.h index 0526bb9c52..5109f748c5 100644 --- a/source/fuzz/transformation_set_function_control.h +++ b/source/fuzz/transformation_set_function_control.h @@ -15,9 +15,9 @@ #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/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -37,17 +37,20 @@ class TransformationSetFunctionControl : public Transformation { // 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; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) 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; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; private: - opt::Instruction* FindFunctionDefInstruction(opt::IRContext* context) const; + opt::Instruction* FindFunctionDefInstruction( + opt::IRContext* ir_context) const; protobufs::TransformationSetFunctionControl message_; }; diff --git a/source/fuzz/transformation_set_loop_control.cpp b/source/fuzz/transformation_set_loop_control.cpp index 9062f174a2..845ac69e36 100644 --- a/source/fuzz/transformation_set_loop_control.cpp +++ b/source/fuzz/transformation_set_loop_control.cpp @@ -31,9 +31,9 @@ TransformationSetLoopControl::TransformationSetLoopControl( } bool TransformationSetLoopControl::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // |message_.block_id| must identify a block that ends with OpLoopMerge. - auto block = context->get_instr_block(message_.block_id()); + auto block = ir_context->get_instr_block(message_.block_id()); if (!block) { return false; } @@ -79,7 +79,8 @@ bool TransformationSetLoopControl::IsApplicable( if ((message_.loop_control() & (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) && - !(PeelCountIsSupported(context) && PartialCountIsSupported(context))) { + !(PeelCountIsSupported(ir_context) && + PartialCountIsSupported(ir_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; @@ -104,11 +105,11 @@ bool TransformationSetLoopControl::IsApplicable( (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask))); } -void TransformationSetLoopControl::Apply(opt::IRContext* context, - FactManager* /*unused*/) const { +void TransformationSetLoopControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { // Grab the loop merge instruction and its associated loop control mask. auto merge_inst = - context->get_instr_block(message_.block_id())->GetMergeInst(); + ir_context->get_instr_block(message_.block_id())->GetMergeInst(); auto existing_loop_control_mask = merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex); @@ -181,11 +182,11 @@ bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation( } bool TransformationSetLoopControl::PartialCountIsSupported( - opt::IRContext* context) { + opt::IRContext* ir_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()) { + switch (ir_context->grammar().target_env()) { case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: @@ -197,11 +198,11 @@ bool TransformationSetLoopControl::PartialCountIsSupported( } bool TransformationSetLoopControl::PeelCountIsSupported( - opt::IRContext* context) { + opt::IRContext* ir_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()) { + switch (ir_context->grammar().target_env()) { case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: diff --git a/source/fuzz/transformation_set_loop_control.h b/source/fuzz/transformation_set_loop_control.h index 28b148cad2..f0c364f985 100644 --- a/source/fuzz/transformation_set_loop_control.h +++ b/source/fuzz/transformation_set_loop_control.h @@ -15,9 +15,9 @@ #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/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -38,13 +38,14 @@ class TransformationSetLoopControl : public Transformation { // 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 + // |ir_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; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // - The loop control operand of the OpLoopMergeInstruction in // |message_.block_id| is overwritten with |message_.loop_control|. @@ -52,16 +53,17 @@ class TransformationSetLoopControl : public Transformation { // 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; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) 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); + static bool PartialCountIsSupported(opt::IRContext* ir_context); // Does the version of SPIR-V being used support the PeelCount loop control? - static bool PeelCountIsSupported(opt::IRContext* context); + static bool PeelCountIsSupported(opt::IRContext* ir_context); private: // Returns true if and only if |loop_single_bit_mask| is *not* set in diff --git a/source/fuzz/transformation_set_memory_operands_mask.cpp b/source/fuzz/transformation_set_memory_operands_mask.cpp index a14e1a6098..131a49942e 100644 --- a/source/fuzz/transformation_set_memory_operands_mask.cpp +++ b/source/fuzz/transformation_set_memory_operands_mask.cpp @@ -42,8 +42,7 @@ TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask( } bool TransformationSetMemoryOperandsMask::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { if (message_.memory_operands_mask_index() != 0) { // The following conditions should never be violated, even if // transformations end up being replayed in a different way to the manner in @@ -54,11 +53,11 @@ bool TransformationSetMemoryOperandsMask::IsApplicable( SpvOpCopyMemory || message_.memory_access_instruction().target_instruction_opcode() == SpvOpCopyMemorySized); - assert(MultipleMemoryOperandMasksAreSupported(context)); + assert(MultipleMemoryOperandMasksAreSupported(ir_context)); } auto instruction = - FindInstruction(message_.memory_access_instruction(), context); + FindInstruction(message_.memory_access_instruction(), ir_context); if (!instruction) { return false; } @@ -94,9 +93,9 @@ bool TransformationSetMemoryOperandsMask::IsApplicable( } void TransformationSetMemoryOperandsMask::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { auto instruction = - FindInstruction(message_.memory_access_instruction(), context); + FindInstruction(message_.memory_access_instruction(), ir_context); auto original_mask_in_operand_index = GetInOperandIndexForMask( *instruction, message_.memory_operands_mask_index()); // Either add a new operand, if no mask operand was already present, or @@ -182,11 +181,11 @@ uint32_t TransformationSetMemoryOperandsMask::GetInOperandIndexForMask( } bool TransformationSetMemoryOperandsMask:: - MultipleMemoryOperandMasksAreSupported(opt::IRContext* context) { + MultipleMemoryOperandMasksAreSupported(opt::IRContext* ir_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()) { + switch (ir_context->grammar().target_env()) { case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: diff --git a/source/fuzz/transformation_set_memory_operands_mask.h b/source/fuzz/transformation_set_memory_operands_mask.h index 20ae145787..9f5081bf33 100644 --- a/source/fuzz/transformation_set_memory_operands_mask.h +++ b/source/fuzz/transformation_set_memory_operands_mask.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ #define SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -40,14 +40,16 @@ class TransformationSetMemoryOperandsMask : public Transformation { // - |message_.memory_operands_mask| must be identical to the original memory // operands mask, except that Volatile may be added, and Nontemporal may be // toggled. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Replaces the operands mask identified by // |message_.memory_operands_mask_index| in the instruction described by // |message_.memory_access_instruction| with |message_.memory_operands_mask|, // creating an input operand for the mask if no such operand was present. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; @@ -57,7 +59,8 @@ class TransformationSetMemoryOperandsMask : public Transformation { // Does the version of SPIR-V being used support multiple memory operand // masks on relevant memory access instructions? - static bool MultipleMemoryOperandMasksAreSupported(opt::IRContext* context); + static bool MultipleMemoryOperandMasksAreSupported( + opt::IRContext* ir_context); // Helper function to get the input operand index associated with mask number // |mask_index|. This is a bit tricky if there are multiple masks, because the diff --git a/source/fuzz/transformation_set_selection_control.cpp b/source/fuzz/transformation_set_selection_control.cpp index ebabdef209..bee1e35756 100644 --- a/source/fuzz/transformation_set_selection_control.cpp +++ b/source/fuzz/transformation_set_selection_control.cpp @@ -28,13 +28,13 @@ TransformationSetSelectionControl::TransformationSetSelectionControl( } bool TransformationSetSelectionControl::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*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 block = ir_context->get_instr_block(message_.block_id())) { if (auto merge_inst = block->GetMergeInst()) { return merge_inst->opcode() == SpvOpSelectionMerge; } @@ -43,9 +43,9 @@ bool TransformationSetSelectionControl::IsApplicable( return false; } -void TransformationSetSelectionControl::Apply(opt::IRContext* context, - FactManager* /*unused*/) const { - context->get_instr_block(message_.block_id()) +void TransformationSetSelectionControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ir_context->get_instr_block(message_.block_id()) ->GetMergeInst() ->SetInOperand(1, {message_.selection_control()}); } diff --git a/source/fuzz/transformation_set_selection_control.h b/source/fuzz/transformation_set_selection_control.h index 19e0c3cfd8..21fbdda454 100644 --- a/source/fuzz/transformation_set_selection_control.h +++ b/source/fuzz/transformation_set_selection_control.h @@ -15,9 +15,9 @@ #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/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -35,12 +35,14 @@ class TransformationSetSelectionControl : public Transformation { // instruction. // - |message_.selection_control| must be one of None, Flatten or // DontFlatten. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) 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; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp index fc5229edc9..3de081e036 100644 --- a/source/fuzz/transformation_split_block.cpp +++ b/source/fuzz/transformation_split_block.cpp @@ -35,18 +35,19 @@ TransformationSplitBlock::TransformationSplitBlock( } bool TransformationSplitBlock::IsApplicable( - opt::IRContext* context, const FactManager& /*unused*/) const { - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { // We require the id for the new block to be unused. return false; } auto instruction_to_split_before = - FindInstruction(message_.instruction_to_split_before(), context); + FindInstruction(message_.instruction_to_split_before(), ir_context); if (!instruction_to_split_before) { // The instruction describing the block we should split does not exist. return false; } - auto block_to_split = context->get_instr_block(instruction_to_split_before); + auto block_to_split = + ir_context->get_instr_block(instruction_to_split_before); assert(block_to_split && "We should not have managed to find the " "instruction if it was not contained in a block."); @@ -79,12 +80,13 @@ bool TransformationSplitBlock::IsApplicable( split_before->NumInOperands() != 2); } -void TransformationSplitBlock::Apply(opt::IRContext* context, - FactManager* fact_manager) const { +void TransformationSplitBlock::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { opt::Instruction* instruction_to_split_before = - FindInstruction(message_.instruction_to_split_before(), context); + FindInstruction(message_.instruction_to_split_before(), ir_context); opt::BasicBlock* block_to_split = - context->get_instr_block(instruction_to_split_before); + ir_context->get_instr_block(instruction_to_split_before); auto split_before = fuzzerutil::GetIteratorForInstruction( block_to_split, instruction_to_split_before); assert(split_before != block_to_split->end() && @@ -93,14 +95,14 @@ void TransformationSplitBlock::Apply(opt::IRContext* context, // We need to make sure the module's id bound is large enough to add the // fresh id. - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // Split the block. - auto new_bb = block_to_split->SplitBasicBlock(context, message_.fresh_id(), + auto new_bb = block_to_split->SplitBasicBlock(ir_context, message_.fresh_id(), split_before); // The split does not automatically add a branch between the two parts of // the original block, so we add one. block_to_split->AddInstruction(MakeUnique( - context, SpvOpBranch, 0, 0, + ir_context, SpvOpBranch, 0, 0, std::initializer_list{opt::Operand( spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})})); // If we split before OpPhi instructions, we need to update their @@ -117,12 +119,15 @@ void TransformationSplitBlock::Apply(opt::IRContext* context, // If the block being split was dead, the new block arising from the split is // also dead. - if (fact_manager->BlockIsDead(block_to_split->id())) { - fact_manager->AddFactBlockIsDead(message_.fresh_id()); + if (transformation_context->GetFactManager()->BlockIsDead( + block_to_split->id())) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + message_.fresh_id()); } // Invalidate all analyses - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationSplitBlock::ToMessage() const { diff --git a/source/fuzz/transformation_split_block.h b/source/fuzz/transformation_split_block.h index a193fc7b8f..3bf6dfd20a 100644 --- a/source/fuzz/transformation_split_block.h +++ b/source/fuzz/transformation_split_block.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ #define SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -40,8 +40,9 @@ class TransformationSplitBlock : public Transformation { // - Splitting 'blk' at 'inst', so that all instructions from 'inst' onwards // appear in a new block that 'blk' directly jumps to must be valid. // - |message_.fresh_id| must not be used by the module. - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // - A new block with label |message_.fresh_id| is inserted right after 'blk' // in program order. @@ -49,7 +50,8 @@ class TransformationSplitBlock : public Transformation { // block. // - 'blk' is made to jump unconditionally to the new block. // - If 'blk' was dead, the new block is also dead. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp index 7cb761126f..3df1b7d37d 100644 --- a/source/fuzz/transformation_store.cpp +++ b/source/fuzz/transformation_store.cpp @@ -34,16 +34,16 @@ TransformationStore::TransformationStore( } bool TransformationStore::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& fact_manager) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // The pointer must exist and have a type. - auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id()); + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); if (!pointer || !pointer->type_id()) { return false; } // The pointer type must indeed be a pointer. - auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id()); + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); assert(pointer_type && "Type id must be defined."); if (pointer_type->opcode() != SpvOpTypePointer) { return false; @@ -65,7 +65,7 @@ bool TransformationStore::IsApplicable( // Determine which instruction we should be inserting before. auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); // It must exist, ... if (!insert_before) { return false; @@ -79,14 +79,15 @@ bool TransformationStore::IsApplicable( // The block we are inserting into needs to be dead, or else the pointee type // of the pointer we are storing to needs to be irrelevant (otherwise the // store could impact on the observable behaviour of the module). - if (!fact_manager.BlockIsDead( - context->get_instr_block(insert_before)->id()) && - !fact_manager.PointeeValueIsIrrelevant(message_.pointer_id())) { + if (!transformation_context.GetFactManager()->BlockIsDead( + ir_context->get_instr_block(insert_before)->id()) && + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + message_.pointer_id())) { return false; } // The value being stored needs to exist and have a type. - auto value = context->get_def_use_mgr()->GetDef(message_.value_id()); + auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id()); if (!value || !value->type_id()) { return false; } @@ -97,25 +98,25 @@ bool TransformationStore::IsApplicable( } // The pointer needs to be available at the insertion point. - if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.pointer_id())) { return false; } // The value needs to be available at the insertion point. - return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before, + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.value_id()); } -void TransformationStore::Apply(opt::IRContext* context, - spvtools::fuzz::FactManager* /*unused*/) const { - FindInstruction(message_.instruction_to_insert_before(), context) +void TransformationStore::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + FindInstruction(message_.instruction_to_insert_before(), ir_context) ->InsertBefore(MakeUnique( - context, SpvOpStore, 0, 0, + ir_context, SpvOpStore, 0, 0, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); - context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationStore::ToMessage() const { diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h index 699afdda03..6746aab5e1 100644 --- a/source/fuzz/transformation_store.h +++ b/source/fuzz/transformation_store.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_STORE_H_ #define SOURCE_FUZZ_TRANSFORMATION_STORE_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -42,14 +42,16 @@ class TransformationStore : public Transformation { // to dominance rules) // - Either the insertion point must be in a dead block, or it must be known // that the pointee value of |message_.pointer_id| is irrelevant - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Adds an instruction of the form: // OpStore |pointer_id| |value_id| // before the instruction identified by // |message_.instruction_to_insert_before|. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_swap_commutable_operands.cpp b/source/fuzz/transformation_swap_commutable_operands.cpp index 49d9de8325..b7622a2397 100644 --- a/source/fuzz/transformation_swap_commutable_operands.cpp +++ b/source/fuzz/transformation_swap_commutable_operands.cpp @@ -31,10 +31,10 @@ TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( } bool TransformationSwapCommutableOperands::IsApplicable( - opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/ + opt::IRContext* ir_context, const TransformationContext& /*unused*/ ) const { auto instruction = - FindInstruction(message_.instruction_descriptor(), context); + FindInstruction(message_.instruction_descriptor(), ir_context); if (instruction == nullptr) return false; SpvOp opcode = static_cast( @@ -46,10 +46,10 @@ bool TransformationSwapCommutableOperands::IsApplicable( } void TransformationSwapCommutableOperands::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/ + opt::IRContext* ir_context, TransformationContext* /*unused*/ ) const { auto instruction = - FindInstruction(message_.instruction_descriptor(), context); + FindInstruction(message_.instruction_descriptor(), ir_context); // By design, the instructions defined to be commutative have exactly two // input parameters. std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1)); diff --git a/source/fuzz/transformation_swap_commutable_operands.h b/source/fuzz/transformation_swap_commutable_operands.h index 061e92dac8..7fe5b70906 100644 --- a/source/fuzz/transformation_swap_commutable_operands.h +++ b/source/fuzz/transformation_swap_commutable_operands.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ #define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -33,11 +33,13 @@ class TransformationSwapCommutableOperands : public Transformation { // - |message_.instruction_descriptor| must identify an existing // commutative instruction - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Swaps the commutable operands. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/source/fuzz/transformation_toggle_access_chain_instruction.cpp index ace331aa28..ca24a18a94 100644 --- a/source/fuzz/transformation_toggle_access_chain_instruction.cpp +++ b/source/fuzz/transformation_toggle_access_chain_instruction.cpp @@ -33,10 +33,10 @@ TransformationToggleAccessChainInstruction:: } bool TransformationToggleAccessChainInstruction::IsApplicable( - opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/ + opt::IRContext* ir_context, const TransformationContext& /*unused*/ ) const { auto instruction = - FindInstruction(message_.instruction_descriptor(), context); + FindInstruction(message_.instruction_descriptor(), ir_context); if (instruction == nullptr) { return false; } @@ -56,10 +56,10 @@ bool TransformationToggleAccessChainInstruction::IsApplicable( } void TransformationToggleAccessChainInstruction::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/ + opt::IRContext* ir_context, TransformationContext* /*unused*/ ) const { auto instruction = - FindInstruction(message_.instruction_descriptor(), context); + FindInstruction(message_.instruction_descriptor(), ir_context); SpvOp opcode = instruction->opcode(); if (opcode == SpvOpAccessChain) { diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.h b/source/fuzz/transformation_toggle_access_chain_instruction.h index 125e1ab038..9cd8fd6e95 100644 --- a/source/fuzz/transformation_toggle_access_chain_instruction.h +++ b/source/fuzz/transformation_toggle_access_chain_instruction.h @@ -15,9 +15,9 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ #define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" namespace spvtools { @@ -33,11 +33,13 @@ class TransformationToggleAccessChainInstruction : public Transformation { // - |message_.instruction_descriptor| must identify an existing // access chain instruction - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Toggles the access chain instruction. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp index e2d889d580..ee6429266c 100644 --- a/source/fuzz/transformation_vector_shuffle.cpp +++ b/source/fuzz/transformation_vector_shuffle.cpp @@ -39,38 +39,37 @@ TransformationVectorShuffle::TransformationVectorShuffle( } bool TransformationVectorShuffle::IsApplicable( - opt::IRContext* context, - const spvtools::fuzz::FactManager& /*unused*/) const { + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // The fresh id must not already be in use. - if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } // The instruction before which the shuffle will be inserted must exist. auto instruction_to_insert_before = - FindInstruction(message_.instruction_to_insert_before(), context); + FindInstruction(message_.instruction_to_insert_before(), ir_context); if (!instruction_to_insert_before) { return false; } // The first vector must be an instruction with a type id auto vector1_instruction = - context->get_def_use_mgr()->GetDef(message_.vector1()); + ir_context->get_def_use_mgr()->GetDef(message_.vector1()); if (!vector1_instruction || !vector1_instruction->type_id()) { return false; } // The second vector must be an instruction with a type id auto vector2_instruction = - context->get_def_use_mgr()->GetDef(message_.vector2()); + ir_context->get_def_use_mgr()->GetDef(message_.vector2()); if (!vector2_instruction || !vector2_instruction->type_id()) { return false; } auto vector1_type = - context->get_type_mgr()->GetType(vector1_instruction->type_id()); + ir_context->get_type_mgr()->GetType(vector1_instruction->type_id()); // The first vector instruction's type must actually be a vector type. if (!vector1_type->AsVector()) { return false; } auto vector2_type = - context->get_type_mgr()->GetType(vector2_instruction->type_id()); + ir_context->get_type_mgr()->GetType(vector2_instruction->type_id()); // The second vector instruction's type must actually be a vector type. if (!vector2_type->AsVector()) { return false; @@ -92,14 +91,14 @@ bool TransformationVectorShuffle::IsApplicable( } // The module must already declare an appropriate type in which to store the // result of the shuffle. - if (!GetResultTypeId(context, *vector1_type->AsVector()->element_type())) { + if (!GetResultTypeId(ir_context, *vector1_type->AsVector()->element_type())) { return false; } // Each of the vectors used in the shuffle must be available at the insertion // point. for (auto used_instruction : {vector1_instruction, vector2_instruction}) { - if (auto block = context->get_instr_block(used_instruction)) { - if (!context->GetDominatorAnalysis(block->GetParent()) + if (auto block = ir_context->get_instr_block(used_instruction)) { + if (!ir_context->GetDominatorAnalysis(block->GetParent()) ->Dominates(used_instruction, instruction_to_insert_before)) { return false; } @@ -113,7 +112,8 @@ bool TransformationVectorShuffle::IsApplicable( } void TransformationVectorShuffle::Apply( - opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // Make input operands for a shuffle instruction - these comprise the two // vectors being shuffled, followed by the integer literal components. opt::Instruction::OperandList shuffle_operands = { @@ -125,16 +125,18 @@ void TransformationVectorShuffle::Apply( } uint32_t result_type_id = GetResultTypeId( - context, *GetVectorType(context, message_.vector1())->element_type()); + ir_context, + *GetVectorType(ir_context, message_.vector1())->element_type()); // Add a shuffle instruction right before the instruction identified by // |message_.instruction_to_insert_before|. - FindInstruction(message_.instruction_to_insert_before(), context) + FindInstruction(message_.instruction_to_insert_before(), ir_context) ->InsertBefore(MakeUnique( - context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(), + ir_context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(), shuffle_operands)); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); // Add synonym facts relating the defined elements of the shuffle result to // the vector components that they come from. @@ -158,24 +160,26 @@ void TransformationVectorShuffle::Apply( // Get a data descriptor for the component of the input vector to which // |component| refers. if (component < - GetVectorType(context, message_.vector1())->element_count()) { + GetVectorType(ir_context, message_.vector1())->element_count()) { descriptor_for_source_component = MakeDataDescriptor(message_.vector1(), {component}); } else { auto index_into_vector_2 = component - - GetVectorType(context, message_.vector1())->element_count(); - assert(index_into_vector_2 < - GetVectorType(context, message_.vector2())->element_count() && - "Vector shuffle index is out of bounds."); + GetVectorType(ir_context, message_.vector1())->element_count(); + assert( + index_into_vector_2 < + GetVectorType(ir_context, message_.vector2())->element_count() && + "Vector shuffle index is out of bounds."); descriptor_for_source_component = MakeDataDescriptor(message_.vector2(), {index_into_vector_2}); } // Add a fact relating this input vector component with the associated // result component. - fact_manager->AddFactDataSynonym(descriptor_for_result_component, - descriptor_for_source_component, context); + transformation_context->GetFactManager()->AddFactDataSynonym( + descriptor_for_result_component, descriptor_for_source_component, + ir_context); } } @@ -186,16 +190,16 @@ protobufs::Transformation TransformationVectorShuffle::ToMessage() const { } uint32_t TransformationVectorShuffle::GetResultTypeId( - opt::IRContext* context, const opt::analysis::Type& element_type) const { + opt::IRContext* ir_context, const opt::analysis::Type& element_type) const { opt::analysis::Vector result_type( &element_type, static_cast(message_.component_size())); - return context->get_type_mgr()->GetId(&result_type); + return ir_context->get_type_mgr()->GetId(&result_type); } opt::analysis::Vector* TransformationVectorShuffle::GetVectorType( - opt::IRContext* context, uint32_t id_of_vector) { - return context->get_type_mgr() - ->GetType(context->get_def_use_mgr()->GetDef(id_of_vector)->type_id()) + opt::IRContext* ir_context, uint32_t id_of_vector) { + return ir_context->get_type_mgr() + ->GetType(ir_context->get_def_use_mgr()->GetDef(id_of_vector)->type_id()) ->AsVector(); } diff --git a/source/fuzz/transformation_vector_shuffle.h b/source/fuzz/transformation_vector_shuffle.h index 81ed227039..f73fc31120 100644 --- a/source/fuzz/transformation_vector_shuffle.h +++ b/source/fuzz/transformation_vector_shuffle.h @@ -15,10 +15,11 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ #define SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ -#include "source/fuzz/fact_manager.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" + #include "source/opt/types.h" namespace spvtools { @@ -45,8 +46,9 @@ class TransformationVectorShuffle : public Transformation { // - The module must already contain a vector type with the same element type // as |message_.vector1| and |message_.vector2|, and with the size of // |message_component| as its element count - bool IsApplicable(opt::IRContext* context, - const FactManager& fact_manager) const override; + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; // Inserts an OpVectorShuffle instruction before // |message_.instruction_to_insert_before|, shuffles vectors @@ -58,19 +60,20 @@ class TransformationVectorShuffle : public Transformation { // result vector is a contiguous sub-range of one of the input vectors, a // fact is added to record that |message_.fresh_id| is synonymous with this // sub-range. - void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; protobufs::Transformation ToMessage() const override; private: - // Returns a type id that already exists in |context| suitable for + // Returns a type id that already exists in |ir_context| suitable for // representing the result of the shuffle, where |element_type| is known to // be the common element type of the vectors to which the shuffle is being // applied. Returns 0 if no such id exists. - uint32_t GetResultTypeId(opt::IRContext* context, + uint32_t GetResultTypeId(opt::IRContext* ir_context, const opt::analysis::Type& element_type) const; - static opt::analysis::Vector* GetVectorType(opt::IRContext* context, + static opt::analysis::Vector* GetVectorType(opt::IRContext* ir_context, uint32_t id_of_vector); protobufs::TransformationVectorShuffle message_; diff --git a/test/fuzz/data_synonym_transformation_test.cpp b/test/fuzz/data_synonym_transformation_test.cpp index 21ea068ffc..2d26d1701a 100644 --- a/test/fuzz/data_synonym_transformation_test.cpp +++ b/test/fuzz/data_synonym_transformation_test.cpp @@ -123,13 +123,24 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - fact_manager.AddFact(MakeSynonymFact(12, {}, 100, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(13, {}, 100, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(22, {}, 100, {2}), context.get()); - fact_manager.AddFact(MakeSynonymFact(28, {}, 101, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(23, {}, 101, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(32, {}, 101, {2}), context.get()); - fact_manager.AddFact(MakeSynonymFact(23, {}, 101, {3}), context.get()); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(12, {}, 100, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(13, {}, 100, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(22, {}, 100, {2}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(28, {}, 101, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(23, {}, 101, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(32, {}, 101, {2}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(23, {}, 101, {3}), context.get()); // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12' auto instruction_descriptor_1 = @@ -139,13 +150,16 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { // Bad: id already in use auto bad_extract_1 = TransformationCompositeExtract( MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 25, 100, {0}); - ASSERT_TRUE(good_extract_1.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(bad_extract_1.IsApplicable(context.get(), fact_manager)); - good_extract_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + good_extract_1.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_extract_1.IsApplicable(context.get(), transformation_context)); + good_extract_1.Apply(context.get(), &transformation_context); auto replacement_1 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(12, instruction_descriptor_1, 1), 102); - ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); - replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %13 with %100[1] in 'OpStore %15 %13' @@ -153,12 +167,14 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { auto good_extract_2 = TransformationCompositeExtract(instruction_descriptor_2, 103, 100, {1}); // No bad example provided here. - ASSERT_TRUE(good_extract_2.IsApplicable(context.get(), fact_manager)); - good_extract_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + good_extract_2.IsApplicable(context.get(), transformation_context)); + good_extract_2.Apply(context.get(), &transformation_context); auto replacement_2 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(13, instruction_descriptor_2, 1), 103); - ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); - replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22' @@ -166,16 +182,19 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { MakeInstructionDescriptor(23, SpvOpConvertSToF, 0); auto good_extract_3 = TransformationCompositeExtract(instruction_descriptor_3, 104, 100, {2}); - ASSERT_TRUE(good_extract_3.IsApplicable(context.get(), fact_manager)); - good_extract_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + good_extract_3.IsApplicable(context.get(), transformation_context)); + good_extract_3.Apply(context.get(), &transformation_context); auto replacement_3 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(22, instruction_descriptor_3, 0), 104); // Bad: wrong input operand index auto bad_replacement_3 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(22, instruction_descriptor_3, 1), 104); - ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(bad_replacement_3.IsApplicable(context.get(), fact_manager)); - replacement_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_replacement_3.IsApplicable(context.get(), transformation_context)); + replacement_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %28 with %101[0] in 'OpStore %33 %28' @@ -185,13 +204,16 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { // Bad: instruction descriptor does not identify an appropriate instruction auto bad_extract_4 = TransformationCompositeExtract( MakeInstructionDescriptor(33, SpvOpCopyObject, 0), 105, 101, {0}); - ASSERT_TRUE(good_extract_4.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(bad_extract_4.IsApplicable(context.get(), fact_manager)); - good_extract_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + good_extract_4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_extract_4.IsApplicable(context.get(), transformation_context)); + good_extract_4.Apply(context.get(), &transformation_context); auto replacement_4 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(28, instruction_descriptor_4, 1), 105); - ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager)); - replacement_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_4.IsApplicable(context.get(), transformation_context)); + replacement_4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23' @@ -199,16 +221,19 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { MakeInstructionDescriptor(50, SpvOpCopyObject, 0); auto good_extract_5 = TransformationCompositeExtract(instruction_descriptor_5, 106, 101, {1}); - ASSERT_TRUE(good_extract_5.IsApplicable(context.get(), fact_manager)); - good_extract_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + good_extract_5.IsApplicable(context.get(), transformation_context)); + good_extract_5.Apply(context.get(), &transformation_context); auto replacement_5 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 106); // Bad: wrong synonym fact being used auto bad_replacement_5 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 105); - ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(bad_replacement_5.IsApplicable(context.get(), fact_manager)); - replacement_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_replacement_5.IsApplicable(context.get(), transformation_context)); + replacement_5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %32 with %101[2] in 'OpStore %33 %32' @@ -218,13 +243,16 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { // Bad: id 1001 does not exist auto bad_extract_6 = TransformationCompositeExtract(instruction_descriptor_6, 107, 1001, {2}); - ASSERT_TRUE(good_extract_6.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(bad_extract_6.IsApplicable(context.get(), fact_manager)); - good_extract_6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + good_extract_6.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_extract_6.IsApplicable(context.get(), transformation_context)); + good_extract_6.Apply(context.get(), &transformation_context); auto replacement_6 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(32, instruction_descriptor_6, 1), 107); - ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager)); - replacement_6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_6.IsApplicable(context.get(), transformation_context)); + replacement_6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23' @@ -232,16 +260,19 @@ TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { MakeInstructionDescriptor(51, SpvOpCopyObject, 0); auto good_extract_7 = TransformationCompositeExtract(instruction_descriptor_7, 108, 101, {3}); - ASSERT_TRUE(good_extract_7.IsApplicable(context.get(), fact_manager)); - good_extract_7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + good_extract_7.IsApplicable(context.get(), transformation_context)); + good_extract_7.Apply(context.get(), &transformation_context); auto replacement_7 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(23, instruction_descriptor_7, 0), 108); // Bad: use id 0 is invalid auto bad_replacement_7 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(0, instruction_descriptor_7, 0), 108); - ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(bad_replacement_7.IsApplicable(context.get(), fact_manager)); - replacement_7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_7.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_replacement_7.IsApplicable(context.get(), transformation_context)); + replacement_7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); const std::string after_transformation = R"( @@ -380,32 +411,41 @@ TEST(DataSynonymTransformationTest, MatrixCompositeSynonyms) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - fact_manager.AddFact(MakeSynonymFact(23, {}, 100, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(25, {}, 100, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(50, {}, 100, {2}), context.get()); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(23, {}, 100, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(25, {}, 100, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(50, {}, 100, {2}), context.get()); // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25' auto instruction_descriptor_1 = MakeInstructionDescriptor(26, SpvOpFAdd, 0); auto extract_1 = TransformationCompositeExtract(instruction_descriptor_1, 101, 100, {0}); - ASSERT_TRUE(extract_1.IsApplicable(context.get(), fact_manager)); - extract_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context)); + extract_1.Apply(context.get(), &transformation_context); auto replacement_1 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(23, instruction_descriptor_1, 0), 101); - ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); - replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25' auto instruction_descriptor_2 = MakeInstructionDescriptor(26, SpvOpFAdd, 0); auto extract_2 = TransformationCompositeExtract(instruction_descriptor_2, 102, 100, {1}); - ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager)); - extract_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context)); + extract_2.Apply(context.get(), &transformation_context); auto replacement_2 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(25, instruction_descriptor_2, 1), 102); - ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); - replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); const std::string after_transformation = R"( @@ -541,26 +581,37 @@ TEST(DataSynonymTransformationTest, StructCompositeSynonyms) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - fact_manager.AddFact(MakeSynonymFact(16, {}, 100, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(45, {}, 100, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(27, {}, 101, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(36, {}, 101, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(27, {}, 101, {2}), context.get()); - fact_manager.AddFact(MakeSynonymFact(22, {}, 102, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(15, {}, 102, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(16, {}, 100, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(45, {}, 100, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(27, {}, 101, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(36, {}, 101, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(27, {}, 101, {2}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(22, {}, 102, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(15, {}, 102, {1}), context.get()); // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45' auto instruction_descriptor_1 = MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0); auto extract_1 = TransformationCompositeExtract(instruction_descriptor_1, 201, 100, {1}); - ASSERT_TRUE(extract_1.IsApplicable(context.get(), fact_manager)); - extract_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context)); + extract_1.Apply(context.get(), &transformation_context); auto replacement_1 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(45, instruction_descriptor_1, 1), 201); - ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); - replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace second occurrence of %27 with %101[0] in '%28 = @@ -569,12 +620,13 @@ TEST(DataSynonymTransformationTest, StructCompositeSynonyms) { MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0); auto extract_2 = TransformationCompositeExtract(instruction_descriptor_2, 202, 101, {0}); - ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager)); - extract_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context)); + extract_2.Apply(context.get(), &transformation_context); auto replacement_2 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(27, instruction_descriptor_2, 1), 202); - ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); - replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44' @@ -582,12 +634,13 @@ TEST(DataSynonymTransformationTest, StructCompositeSynonyms) { MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0); auto extract_3 = TransformationCompositeExtract(instruction_descriptor_3, 203, 101, {1}); - ASSERT_TRUE(extract_3.IsApplicable(context.get(), fact_manager)); - extract_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_3.IsApplicable(context.get(), transformation_context)); + extract_3.Apply(context.get(), &transformation_context); auto replacement_3 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(36, instruction_descriptor_3, 0), 203); - ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager)); - replacement_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_3.IsApplicable(context.get(), transformation_context)); + replacement_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct @@ -596,24 +649,26 @@ TEST(DataSynonymTransformationTest, StructCompositeSynonyms) { MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0); auto extract_4 = TransformationCompositeExtract(instruction_descriptor_4, 204, 101, {2}); - ASSERT_TRUE(extract_4.IsApplicable(context.get(), fact_manager)); - extract_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_4.IsApplicable(context.get(), transformation_context)); + extract_4.Apply(context.get(), &transformation_context); auto replacement_4 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(27, instruction_descriptor_4, 0), 204); - ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager)); - replacement_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_4.IsApplicable(context.get(), transformation_context)); + replacement_4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %22 with %102[0] in 'OpStore %23 %22' auto instruction_descriptor_5 = MakeInstructionDescriptor(23, SpvOpStore, 0); auto extract_5 = TransformationCompositeExtract(instruction_descriptor_5, 205, 102, {0}); - ASSERT_TRUE(extract_5.IsApplicable(context.get(), fact_manager)); - extract_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context)); + extract_5.Apply(context.get(), &transformation_context); auto replacement_5 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(22, instruction_descriptor_5, 1), 205); - ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager)); - replacement_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_5.IsApplicable(context.get(), transformation_context)); + replacement_5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); const std::string after_transformation = R"( @@ -816,38 +871,63 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - fact_manager.AddFact(MakeSynonymFact(20, {0}, 100, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(20, {1}, 100, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(20, {2}, 100, {2}), context.get()); - fact_manager.AddFact(MakeSynonymFact(54, {}, 100, {3}), context.get()); - fact_manager.AddFact(MakeSynonymFact(15, {0}, 101, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(15, {1}, 101, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(19, {0}, 101, {2}), context.get()); - fact_manager.AddFact(MakeSynonymFact(19, {1}, 101, {3}), context.get()); - fact_manager.AddFact(MakeSynonymFact(27, {}, 102, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(15, {0}, 102, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(15, {1}, 102, {2}), context.get()); - fact_manager.AddFact(MakeSynonymFact(33, {}, 103, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(47, {0}, 103, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(47, {1}, 103, {2}), context.get()); - fact_manager.AddFact(MakeSynonymFact(47, {2}, 103, {3}), context.get()); - fact_manager.AddFact(MakeSynonymFact(42, {}, 104, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(45, {}, 104, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(38, {0}, 105, {0}), context.get()); - fact_manager.AddFact(MakeSynonymFact(38, {1}, 105, {1}), context.get()); - fact_manager.AddFact(MakeSynonymFact(46, {}, 105, {2}), context.get()); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(20, {0}, 100, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(20, {1}, 100, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(20, {2}, 100, {2}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(54, {}, 100, {3}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(15, {0}, 101, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(15, {1}, 101, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(19, {0}, 101, {2}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(19, {1}, 101, {3}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(27, {}, 102, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(15, {0}, 102, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(15, {1}, 102, {2}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(33, {}, 103, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(47, {0}, 103, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(47, {1}, 103, {2}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(47, {2}, 103, {3}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(42, {}, 104, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(45, {}, 104, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(38, {0}, 105, {0}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(38, {1}, 105, {1}), context.get()); + transformation_context.GetFactManager()->AddFact( + MakeSynonymFact(46, {}, 105, {2}), context.get()); // Replace %20 with %100[0:2] in '%80 = OpCopyObject %16 %20' auto instruction_descriptor_1 = MakeInstructionDescriptor(80, SpvOpCopyObject, 0); auto shuffle_1 = TransformationVectorShuffle(instruction_descriptor_1, 200, 100, 100, {0, 1, 2}); - ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), fact_manager)); - shuffle_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), transformation_context)); + shuffle_1.Apply(context.get(), &transformation_context); auto replacement_1 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(20, instruction_descriptor_1, 0), 200); - ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); - replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55' @@ -856,24 +936,26 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { auto extract_2 = TransformationCompositeExtract(instruction_descriptor_2, 201, 100, {3}); - ASSERT_TRUE(extract_2.IsApplicable(context.get(), fact_manager)); - extract_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context)); + extract_2.Apply(context.get(), &transformation_context); auto replacement_2 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(54, instruction_descriptor_2, 0), 201); - ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); - replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %15 with %101[0:1] in 'OpStore %12 %15' auto instruction_descriptor_3 = MakeInstructionDescriptor(64, SpvOpStore, 0); auto shuffle_3 = TransformationVectorShuffle(instruction_descriptor_3, 202, 101, 101, {0, 1}); - ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), fact_manager)); - shuffle_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), transformation_context)); + shuffle_3.Apply(context.get(), &transformation_context); auto replacement_3 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(15, instruction_descriptor_3, 1), 202); - ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager)); - replacement_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_3.IsApplicable(context.get(), transformation_context)); + replacement_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %19 with %101[2:3] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1' @@ -881,12 +963,13 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0); auto shuffle_4 = TransformationVectorShuffle(instruction_descriptor_4, 203, 101, 101, {2, 3}); - ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), fact_manager)); - shuffle_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), transformation_context)); + shuffle_4.Apply(context.get(), &transformation_context); auto replacement_4 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(19, instruction_descriptor_4, 0), 203); - ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager)); - replacement_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_4.IsApplicable(context.get(), transformation_context)); + replacement_4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28 @@ -896,12 +979,13 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { auto extract_5 = TransformationCompositeExtract(instruction_descriptor_5, 204, 102, {0}); - ASSERT_TRUE(extract_5.IsApplicable(context.get(), fact_manager)); - extract_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context)); + extract_5.Apply(context.get(), &transformation_context); auto replacement_5 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(27, instruction_descriptor_5, 1), 204); - ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager)); - replacement_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_5.IsApplicable(context.get(), transformation_context)); + replacement_5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %15 with %102[1:2] in '%83 = OpCopyObject %10 %15' @@ -909,12 +993,13 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { MakeInstructionDescriptor(83, SpvOpCopyObject, 0); auto shuffle_6 = TransformationVectorShuffle(instruction_descriptor_6, 205, 102, 102, {1, 2}); - ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), fact_manager)); - shuffle_6.Apply(context.get(), &fact_manager); + ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), transformation_context)); + shuffle_6.Apply(context.get(), &transformation_context); auto replacement_6 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(15, instruction_descriptor_6, 0), 205); - ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager)); - replacement_6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_6.IsApplicable(context.get(), transformation_context)); + replacement_6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33' @@ -922,12 +1007,13 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { MakeInstructionDescriptor(86, SpvOpCopyObject, 0); auto extract_7 = TransformationCompositeExtract(instruction_descriptor_7, 206, 103, {0}); - ASSERT_TRUE(extract_7.IsApplicable(context.get(), fact_manager)); - extract_7.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_7.IsApplicable(context.get(), transformation_context)); + extract_7.Apply(context.get(), &transformation_context); auto replacement_7 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(33, instruction_descriptor_7, 0), 206); - ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager)); - replacement_7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_7.IsApplicable(context.get(), transformation_context)); + replacement_7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %47 with %103[1:3] in '%84 = OpCopyObject %39 %47' @@ -935,12 +1021,13 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { MakeInstructionDescriptor(84, SpvOpCopyObject, 0); auto shuffle_8 = TransformationVectorShuffle(instruction_descriptor_8, 207, 103, 103, {1, 2, 3}); - ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), fact_manager)); - shuffle_8.Apply(context.get(), &fact_manager); + ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), transformation_context)); + shuffle_8.Apply(context.get(), &transformation_context); auto replacement_8 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(47, instruction_descriptor_8, 0), 207); - ASSERT_TRUE(replacement_8.IsApplicable(context.get(), fact_manager)); - replacement_8.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_8.IsApplicable(context.get(), transformation_context)); + replacement_8.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42' @@ -948,12 +1035,13 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { MakeInstructionDescriptor(85, SpvOpCopyObject, 0); auto extract_9 = TransformationCompositeExtract(instruction_descriptor_9, 208, 104, {0}); - ASSERT_TRUE(extract_9.IsApplicable(context.get(), fact_manager)); - extract_9.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_9.IsApplicable(context.get(), transformation_context)); + extract_9.Apply(context.get(), &transformation_context); auto replacement_9 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(42, instruction_descriptor_9, 0), 208); - ASSERT_TRUE(replacement_9.IsApplicable(context.get(), fact_manager)); - replacement_9.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_9.IsApplicable(context.get(), transformation_context)); + replacement_9.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46' @@ -961,24 +1049,26 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { MakeInstructionDescriptor(63, SpvOpLogicalOr, 0); auto extract_10 = TransformationCompositeExtract(instruction_descriptor_10, 209, 104, {1}); - ASSERT_TRUE(extract_10.IsApplicable(context.get(), fact_manager)); - extract_10.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_10.IsApplicable(context.get(), transformation_context)); + extract_10.Apply(context.get(), &transformation_context); auto replacement_10 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(45, instruction_descriptor_10, 0), 209); - ASSERT_TRUE(replacement_10.IsApplicable(context.get(), fact_manager)); - replacement_10.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_10.IsApplicable(context.get(), transformation_context)); + replacement_10.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %38 with %105[0:1] in 'OpStore %36 %38' auto instruction_descriptor_11 = MakeInstructionDescriptor(85, SpvOpStore, 0); auto shuffle_11 = TransformationVectorShuffle(instruction_descriptor_11, 210, 105, 105, {0, 1}); - ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), fact_manager)); - shuffle_11.Apply(context.get(), &fact_manager); + ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), transformation_context)); + shuffle_11.Apply(context.get(), &transformation_context); auto replacement_11 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(38, instruction_descriptor_11, 1), 210); - ASSERT_TRUE(replacement_11.IsApplicable(context.get(), fact_manager)); - replacement_11.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_11.IsApplicable(context.get(), transformation_context)); + replacement_11.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46' @@ -986,12 +1076,13 @@ TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0); auto extract_12 = TransformationCompositeExtract(instruction_descriptor_12, 211, 105, {2}); - ASSERT_TRUE(extract_12.IsApplicable(context.get(), fact_manager)); - extract_12.Apply(context.get(), &fact_manager); + ASSERT_TRUE(extract_12.IsApplicable(context.get(), transformation_context)); + extract_12.Apply(context.get(), &transformation_context); auto replacement_12 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(46, instruction_descriptor_12, 1), 211); - ASSERT_TRUE(replacement_12.IsApplicable(context.get(), fact_manager)); - replacement_12.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_12.IsApplicable(context.get(), transformation_context)); + replacement_12.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); const std::string after_transformation = R"( diff --git a/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp b/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp index 89f006e078..90d1c84728 100644 --- a/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp +++ b/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp @@ -64,10 +64,14 @@ TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); protobufs::TransformationSequence transformation_sequence; - FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager, + FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context, &fuzzer_context, &transformation_sequence); pass.Apply(); ASSERT_TRUE(IsValid(env, context.get())); @@ -173,6 +177,10 @@ TEST(FuzzerPassAddUsefulConstructsTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); protobufs::TransformationSequence transformation_sequence; @@ -292,7 +300,7 @@ TEST(FuzzerPassAddUsefulConstructsTest, context->get_constant_mgr()->FindConstant(&int_constant_8)); } - FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager, + FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context, &fuzzer_context, &transformation_sequence); pass.Apply(); ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index dc7ba3a5dc..549dd13fef 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -194,14 +194,17 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) { ASSERT_TRUE(IsValid(env, donor_context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto prng = MakeUnique(0); FuzzerContext fuzzer_context(prng.get(), 100); protobufs::TransformationSequence transformation_sequence; - FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager, - &fuzzer_context, &transformation_sequence, - {}); + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -269,13 +272,16 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { ASSERT_TRUE(IsValid(env, donor_context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); protobufs::TransformationSequence transformation_sequence; - FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager, - &fuzzer_context, &transformation_sequence, - {}); + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -393,13 +399,16 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { ASSERT_TRUE(IsValid(env, donor_context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); protobufs::TransformationSequence transformation_sequence; - FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager, - &fuzzer_context, &transformation_sequence, - {}); + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -481,13 +490,16 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { ASSERT_TRUE(IsValid(env, donor_context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); protobufs::TransformationSequence transformation_sequence; - FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager, - &fuzzer_context, &transformation_sequence, - {}); + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -658,13 +670,16 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { ASSERT_TRUE(IsValid(env, donor_context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); protobufs::TransformationSequence transformation_sequence; - FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &fact_manager, - &fuzzer_context, &transformation_sequence, - {}); + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp index 516d371b49..443c31c791 100644 --- a/test/fuzz/transformation_access_chain_test.cpp +++ b/test/fuzz/transformation_access_chain_test.cpp @@ -118,169 +118,194 @@ TEST(TransformationAccessChainTest, BasicTest) { // Indices 0-5 are in ids 80-85 FactManager fact_manager; - fact_manager.AddFactValueOfPointeeIsIrrelevant(54); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 54); // Bad: id is not fresh ASSERT_FALSE(TransformationAccessChain( 43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist ASSERT_FALSE(TransformationAccessChain( 100, 1000, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id is not a type ASSERT_FALSE(TransformationAccessChain( 100, 5, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id is not a pointer ASSERT_FALSE(TransformationAccessChain( 100, 23, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: index id does not exist ASSERT_FALSE(TransformationAccessChain( 100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: index id is not a constant ASSERT_FALSE(TransformationAccessChain( 100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: too many indices ASSERT_FALSE( TransformationAccessChain(100, 43, {80, 80, 80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: index id is out of bounds ASSERT_FALSE( TransformationAccessChain(100, 43, {80, 83}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before variable ASSERT_FALSE(TransformationAccessChain( 100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer not available ASSERT_FALSE( TransformationAccessChain( 100, 43, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: instruction descriptor does not identify anything ASSERT_FALSE(TransformationAccessChain( 100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is null ASSERT_FALSE(TransformationAccessChain( 100, 45, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is undef ASSERT_FALSE(TransformationAccessChain( 100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer to result type does not exist ASSERT_FALSE(TransformationAccessChain( 100, 52, {0}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); { TransformationAccessChain transformation( 100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); } { TransformationAccessChain transformation( 101, 28, {81}, MakeInstructionDescriptor(42, SpvOpReturn, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); } { TransformationAccessChain transformation( 102, 36, {80, 81}, MakeInstructionDescriptor(37, SpvOpStore, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); } { TransformationAccessChain transformation( 103, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103)); } { TransformationAccessChain transformation( 104, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104)); } { TransformationAccessChain transformation( 105, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105)); } { TransformationAccessChain transformation( 106, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(106)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(106)); } { TransformationAccessChain transformation( 107, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(107)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107)); } { TransformationAccessChain transformation( 108, 54, {85, 81, 81}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(108)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(108)); } { TransformationAccessChain transformation( 109, 48, {80, 80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(109)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(109)); } std::string after_transformation = R"( @@ -401,19 +426,24 @@ TEST(TransformationAccessChainTest, IsomorphicStructs) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); { TransformationAccessChain transformation( 100, 11, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { TransformationAccessChain transformation( 101, 12, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } diff --git a/test/fuzz/transformation_add_constant_boolean_test.cpp b/test/fuzz/transformation_add_constant_boolean_test.cpp index f51c46bf41..c603333648 100644 --- a/test/fuzz/transformation_add_constant_boolean_test.cpp +++ b/test/fuzz/transformation_add_constant_boolean_test.cpp @@ -43,42 +43,47 @@ TEST(TransformationAddConstantBooleanTest, NeitherPresentInitiallyAddBoth) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // True and false can both be added as neither is present. ASSERT_TRUE(TransformationAddConstantBoolean(7, true).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); ASSERT_TRUE(TransformationAddConstantBoolean(7, false).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // Id 5 is already taken. ASSERT_FALSE(TransformationAddConstantBoolean(5, true).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); auto add_true = TransformationAddConstantBoolean(7, true); auto add_false = TransformationAddConstantBoolean(8, false); - ASSERT_TRUE(add_true.IsApplicable(context.get(), fact_manager)); - add_true.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_true.IsApplicable(context.get(), transformation_context)); + add_true.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Having added true, we cannot add it again with the same id. - ASSERT_FALSE(add_true.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(add_true.IsApplicable(context.get(), transformation_context)); // But we can add it with a different id. auto add_true_again = TransformationAddConstantBoolean(100, true); - ASSERT_TRUE(add_true_again.IsApplicable(context.get(), fact_manager)); - add_true_again.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_true_again.IsApplicable(context.get(), transformation_context)); + add_true_again.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(add_false.IsApplicable(context.get(), fact_manager)); - add_false.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_false.IsApplicable(context.get(), transformation_context)); + add_false.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Having added false, we cannot add it again with the same id. - ASSERT_FALSE(add_false.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(add_false.IsApplicable(context.get(), transformation_context)); // But we can add it with a different id. auto add_false_again = TransformationAddConstantBoolean(101, false); - ASSERT_TRUE(add_false_again.IsApplicable(context.get(), fact_manager)); - add_false_again.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_false_again.IsApplicable(context.get(), transformation_context)); + add_false_again.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -128,12 +133,15 @@ TEST(TransformationAddConstantBooleanTest, NoOpTypeBoolPresent) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Neither true nor false can be added as OpTypeBool is not present. ASSERT_FALSE(TransformationAddConstantBoolean(6, true).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); ASSERT_FALSE(TransformationAddConstantBoolean(6, false).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp index 5ce171ba9b..021bf58ee8 100644 --- a/test/fuzz/transformation_add_constant_composite_test.cpp +++ b/test/fuzz/transformation_add_constant_composite_test.cpp @@ -64,19 +64,22 @@ TEST(TransformationAddConstantCompositeTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Too few ids ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Too many ids ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Id already in use ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %39 is not a type ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationAddConstantComposite transformations[] = { // %100 = OpConstantComposite %7 %11 %12 @@ -101,8 +104,9 @@ TEST(TransformationAddConstantCompositeTest, BasicTest) { TransformationAddConstantComposite(106, 35, {38, 39, 40})}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/transformation_add_constant_scalar_test.cpp b/test/fuzz/transformation_add_constant_scalar_test.cpp index b15611124e..5124b7d84c 100644 --- a/test/fuzz/transformation_add_constant_scalar_test.cpp +++ b/test/fuzz/transformation_add_constant_scalar_test.cpp @@ -62,6 +62,9 @@ TEST(TransformationAddConstantScalarTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const float float_values[2] = {3.0, 30.0}; uint32_t uint_for_float[2]; @@ -87,55 +90,62 @@ TEST(TransformationAddConstantScalarTest, BasicTest) { auto bad_type_id_is_pointer = TransformationAddConstantScalar(111, 11, {0}); // Id is already in use. - ASSERT_FALSE(bad_id_already_used.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_id_already_used.IsApplicable(context.get(), transformation_context)); // At least one word of data must be provided. - ASSERT_FALSE(bad_no_data.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_no_data.IsApplicable(context.get(), transformation_context)); // Cannot give two data words for a 32-bit type. - ASSERT_FALSE(bad_too_much_data.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_too_much_data.IsApplicable(context.get(), transformation_context)); // Type id does not exist - ASSERT_FALSE( - bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_type_id_does_not_exist.IsApplicable(context.get(), + transformation_context)); // Type id is not a type - ASSERT_FALSE( - bad_type_id_is_not_a_type.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_type_id_is_not_a_type.IsApplicable(context.get(), + transformation_context)); // Type id is void - ASSERT_FALSE(bad_type_id_is_void.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_type_id_is_void.IsApplicable(context.get(), transformation_context)); // Type id is pointer - ASSERT_FALSE( - bad_type_id_is_pointer.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_type_id_is_pointer.IsApplicable(context.get(), + transformation_context)); - ASSERT_TRUE(add_signed_int_1.IsApplicable(context.get(), fact_manager)); - add_signed_int_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_signed_int_1.IsApplicable(context.get(), transformation_context)); + add_signed_int_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(add_signed_int_10.IsApplicable(context.get(), fact_manager)); - add_signed_int_10.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_signed_int_10.IsApplicable(context.get(), transformation_context)); + add_signed_int_10.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(add_unsigned_int_2.IsApplicable(context.get(), fact_manager)); - add_unsigned_int_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_unsigned_int_2.IsApplicable(context.get(), transformation_context)); + add_unsigned_int_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(add_unsigned_int_20.IsApplicable(context.get(), fact_manager)); - add_unsigned_int_20.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_unsigned_int_20.IsApplicable(context.get(), transformation_context)); + add_unsigned_int_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(add_float_3.IsApplicable(context.get(), fact_manager)); - add_float_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_float_3.IsApplicable(context.get(), transformation_context)); + add_float_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(add_float_30.IsApplicable(context.get(), fact_manager)); - add_float_30.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_float_30.IsApplicable(context.get(), transformation_context)); + add_float_30.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable( + context.get(), transformation_context)); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp index f89140f817..c9be52090e 100644 --- a/test/fuzz/transformation_add_dead_block_test.cpp +++ b/test/fuzz/transformation_add_dead_block_test.cpp @@ -46,21 +46,25 @@ TEST(TransformationAddDeadBlockTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id 4 is already in use ASSERT_FALSE(TransformationAddDeadBlock(4, 5, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Id 7 is not a block ASSERT_FALSE(TransformationAddDeadBlock(100, 7, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationAddDeadBlock transformation(100, 5, true); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.BlockIsDead(100)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100)); std::string after_transformation = R"( OpCapability Shader @@ -119,9 +123,12 @@ TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeSelectionMerge) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeLoopMergeOrContinue) { @@ -160,13 +167,16 @@ TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeLoopMergeOrContinue) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Bad because 9's successor is the loop continue target. ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad because 10's successor is the loop merge. ASSERT_FALSE(TransformationAddDeadBlock(100, 10, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBlockTest, SourceBlockMustNotBeLoopHead) { @@ -203,10 +213,13 @@ TEST(TransformationAddDeadBlockTest, SourceBlockMustNotBeLoopHead) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Bad because 8 is a loop head. ASSERT_FALSE(TransformationAddDeadBlock(100, 8, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBlockTest, OpPhiInTarget) { @@ -240,13 +253,17 @@ TEST(TransformationAddDeadBlockTest, OpPhiInTarget) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationAddDeadBlock transformation(100, 5, true); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.BlockIsDead(100)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100)); std::string after_transformation = R"( OpCapability Shader @@ -309,11 +326,14 @@ TEST(TransformationAddDeadBlockTest, BackEdge) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // 9 is a back edge block, so it would not be OK to add a dead block here, // as then both 9 and the dead block would branch to the loop header, 8. ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_add_dead_break_test.cpp b/test/fuzz/transformation_add_dead_break_test.cpp index d60fc1fc52..8400b0cfd8 100644 --- a/test/fuzz/transformation_add_dead_break_test.cpp +++ b/test/fuzz/transformation_add_dead_break_test.cpp @@ -100,44 +100,47 @@ TEST(TransformationAddDeadBreakTest, BreaksOutOfSimpleIf) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const uint32_t merge_block = 16; // These are all possibilities. ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable: 100 is not a block id. ASSERT_FALSE(TransformationAddDeadBreak(100, merge_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(15, 100, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable: 24 is not a merge block. ASSERT_FALSE(TransformationAddDeadBreak(15, 24, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // These are the transformations we will apply. auto transformation1 = TransformationAddDeadBreak(15, merge_block, true, {}); @@ -147,28 +150,34 @@ TEST(TransformationAddDeadBreakTest, BreaksOutOfSimpleIf) { auto transformation5 = TransformationAddDeadBreak(23, merge_block, true, {}); auto transformation6 = TransformationAddDeadBreak(24, merge_block, false, {}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); - transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + transformation6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -333,6 +342,9 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfNestedIfs) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // The header and merge blocks const uint32_t header_inner = 34; @@ -354,53 +366,53 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfNestedIfs) { // Fine to break from a construct to its merge ASSERT_TRUE(TransformationAddDeadBreak(inner_block_1, merge_inner, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(inner_block_2, merge_inner, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(outer_block_1, merge_outer, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(outer_block_2, merge_outer, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(outer_block_3, merge_outer, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(outer_block_4, merge_outer, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(after_block_1, merge_after, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(after_block_2, merge_after, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to break to the wrong merge (whether enclosing or not) ASSERT_FALSE(TransformationAddDeadBreak(inner_block_1, merge_outer, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(inner_block_2, merge_after, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, merge_inner, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(outer_block_2, merge_after, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(after_block_1, merge_inner, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(after_block_2, merge_outer, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to break from header (as it does not branch unconditionally) ASSERT_FALSE(TransformationAddDeadBreak(header_inner, merge_inner, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(header_outer, merge_outer, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(header_after, merge_after, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to break to non-merge ASSERT_FALSE( TransformationAddDeadBreak(inner_block_1, inner_block_2, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(outer_block_2, after_block_1, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, header_after, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationAddDeadBreak(inner_block_1, merge_inner, true, {}); @@ -419,36 +431,44 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfNestedIfs) { auto transformation8 = TransformationAddDeadBreak(after_block_2, merge_after, false, {}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); - transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + transformation6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager)); - transformation7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + transformation7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager)); - transformation8.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation8.IsApplicable(context.get(), transformation_context)); + transformation8.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -685,6 +705,9 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfNestedSwitches) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // The header and merge blocks const uint32_t header_outer_if = 5; @@ -715,63 +738,63 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfNestedSwitches) { // Fine to branch straight to direct merge block for a construct ASSERT_TRUE(TransformationAddDeadBreak(then_outer_switch_block_1, merge_then_outer_switch, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_1, merge_then_inner_switch, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_2, merge_then_inner_switch, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_3, merge_then_inner_switch, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_1, merge_else_switch, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_2, merge_else_switch, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_3, merge_else_switch, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationAddDeadBreak(inner_if_1_block_1, merge_inner_if_1, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(inner_if_1_block_2, merge_inner_if_1, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationAddDeadBreak(inner_if_2_block_1, merge_inner_if_2, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to break out of a switch from a selection construct inside the // switch. ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_1, merge_then_outer_switch, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_2, merge_then_outer_switch, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(inner_if_2_block_1, merge_then_outer_switch, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Some miscellaneous inapplicable cases. ASSERT_FALSE( TransformationAddDeadBreak(header_outer_if, merge_outer_if, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_1, inner_if_1_block_2, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(header_then_inner_switch, header_then_outer_switch, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(header_else_switch, then_inner_switch_block_3, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_2, header_inner_if_2, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationAddDeadBreak( then_outer_switch_block_1, merge_then_outer_switch, true, {}); @@ -794,44 +817,54 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfNestedSwitches) { auto transformation10 = TransformationAddDeadBreak( inner_if_2_block_1, merge_inner_if_2, true, {}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); - transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + transformation6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager)); - transformation7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + transformation7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager)); - transformation8.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation8.IsApplicable(context.get(), transformation_context)); + transformation8.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation9.IsApplicable(context.get(), fact_manager)); - transformation9.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation9.IsApplicable(context.get(), transformation_context)); + transformation9.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation10.IsApplicable(context.get(), fact_manager)); - transformation10.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation10.IsApplicable(context.get(), transformation_context)); + transformation10.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1094,6 +1127,9 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfLoopNest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // The header and merge blocks const uint32_t header_do_while = 6; @@ -1123,75 +1159,75 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfLoopNest) { // Fine to break from any loop header to its merge ASSERT_TRUE( TransformationAddDeadBreak(header_do_while, merge_do_while, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(header_for_i, merge_for_i, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadBreak(header_for_j, merge_for_j, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Fine to break from any of the blocks in constructs in the "for j" loop to // that loop's merge ASSERT_TRUE( TransformationAddDeadBreak(block_in_inner_if, merge_for_j, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationAddDeadBreak(block_switch_case, merge_for_j, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationAddDeadBreak(block_switch_default, merge_for_j, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Fine to break from the body of the "for i" loop to that loop's merge ASSERT_TRUE( TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to break from multiple loops ASSERT_FALSE( TransformationAddDeadBreak(block_in_inner_if, merge_do_while, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(block_switch_case, merge_do_while, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_do_while, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(header_for_j, merge_do_while, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to break loop from its continue construct ASSERT_FALSE( TransformationAddDeadBreak(continue_do_while, merge_do_while, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to break out of multiple non-loop constructs if not breaking to a // loop merge ASSERT_FALSE( TransformationAddDeadBreak(block_in_inner_if, merge_if_x_eq_y, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(block_switch_case, merge_if_x_eq_y, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_if_x_eq_y, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Some miscellaneous inapplicable transformations ASSERT_FALSE( TransformationAddDeadBreak(header_if_x_eq_2, header_if_x_eq_y, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(merge_if_x_eq_2, merge_switch, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(header_switch, header_switch, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationAddDeadBreak(header_do_while, merge_do_while, true, {}); @@ -1208,32 +1244,39 @@ TEST(TransformationAddDeadBreakTest, BreakOutOfLoopNest) { auto transformation7 = TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); - transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + transformation6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager)); - transformation7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + transformation7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1421,12 +1464,15 @@ TEST(TransformationAddDeadBreakTest, NoBreakFromContinueConstruct) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Not OK to break loop from its continue construct ASSERT_FALSE(TransformationAddDeadBreak(13, 12, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(23, 12, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) { @@ -1509,6 +1555,9 @@ TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const uint32_t loop_merge = 12; const uint32_t selection_merge = 24; @@ -1520,13 +1569,13 @@ TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) { // Not OK to jump from the selection to the loop merge, as this would break // from the loop's continue construct. ASSERT_FALSE(TransformationAddDeadBreak(in_selection_1, loop_merge, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(in_selection_2, loop_merge, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(in_selection_3, loop_merge, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationAddDeadBreak(in_selection_4, loop_merge, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // But fine to jump from the selection to its merge. @@ -1539,20 +1588,24 @@ TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) { auto transformation4 = TransformationAddDeadBreak(in_selection_4, selection_merge, true, {}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1720,6 +1773,9 @@ TEST(TransformationAddDeadBreakTest, LoopInContinueConstruct) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const uint32_t outer_loop_merge = 34; const uint32_t outer_loop_block = 33; @@ -1729,22 +1785,24 @@ TEST(TransformationAddDeadBreakTest, LoopInContinueConstruct) { // Some inapplicable cases ASSERT_FALSE( TransformationAddDeadBreak(inner_loop_block, outer_loop_merge, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationAddDeadBreak(outer_loop_block, inner_loop_merge, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationAddDeadBreak(inner_loop_block, inner_loop_merge, true, {}); auto transformation2 = TransformationAddDeadBreak(outer_loop_block, outer_loop_merge, true, {}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1936,36 +1994,39 @@ TEST(TransformationAddDeadBreakTest, PhiInstructions) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Some inapplicable transformations // Not applicable because there is already an edge 19->20, so the OpPhis at 20 // do not need to be updated ASSERT_FALSE(TransformationAddDeadBreak(19, 20, true, {13, 21}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not applicable because two OpPhis (not zero) need to be updated at 20 ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not applicable because two OpPhis (not just one) need to be updated at 20 ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not applicable because the given ids do not have types that match the // OpPhis at 20, in order ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 13}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not applicable because id 23 is a label ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 23}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not applicable because 101 is not an id ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 101}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not applicable because ids 51 and 47 are not available at the end of block // 23 ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {51, 47}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not applicable because OpConstantFalse is not present in the module ASSERT_FALSE(TransformationAddDeadBreak(19, 20, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationAddDeadBreak(19, 20, true, {}); auto transformation2 = TransformationAddDeadBreak(23, 20, true, {13, 21}); @@ -1973,24 +2034,29 @@ TEST(TransformationAddDeadBreakTest, PhiInstructions) { auto transformation4 = TransformationAddDeadBreak(30, 31, true, {21, 13}); auto transformation5 = TransformationAddDeadBreak(75, 31, true, {47, 51}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -2119,9 +2185,13 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules1) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBreakTest, RespectDominanceRules2) { @@ -2172,9 +2242,13 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules2) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBreakTest, RespectDominanceRules3) { @@ -2219,11 +2293,15 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules3) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto good_transformation = TransformationAddDeadBreak(100, 101, false, {11}); - ASSERT_TRUE(good_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + good_transformation.IsApplicable(context.get(), transformation_context)); - good_transformation.Apply(context.get(), &fact_manager); + good_transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -2307,11 +2385,15 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules4) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto good_transformation = TransformationAddDeadBreak(102, 101, false, {11}); - ASSERT_TRUE(good_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + good_transformation.IsApplicable(context.get(), transformation_context)); - good_transformation.Apply(context.get(), &fact_manager); + good_transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -2389,9 +2471,13 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules5) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBreakTest, RespectDominanceRules6) { @@ -2446,9 +2532,13 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules6) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBreakTest, RespectDominanceRules7) { @@ -2505,9 +2595,13 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules7) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBreakTest, RespectDominanceRules8) { @@ -2551,9 +2645,13 @@ TEST(TransformationAddDeadBreakTest, RespectDominanceRules8) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadBreakTest, @@ -2597,12 +2695,16 @@ TEST(TransformationAddDeadBreakTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Bad because 14 comes before 12 in the module, and 14 has no predecessors. // This means that an edge from 12 to 14 will lead to 12 dominating 14, which // is illegal if 12 appears after 14. auto bad_transformation = TransformationAddDeadBreak(12, 14, true, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_add_dead_continue_test.cpp b/test/fuzz/transformation_add_dead_continue_test.cpp index ff93da8a92..07ee3b180d 100644 --- a/test/fuzz/transformation_add_dead_continue_test.cpp +++ b/test/fuzz/transformation_add_dead_continue_test.cpp @@ -97,57 +97,63 @@ TEST(TransformationAddDeadContinueTest, SimpleExample) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // These are all possibilities. ASSERT_TRUE(TransformationAddDeadContinue(11, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadContinue(11, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadContinue(12, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadContinue(12, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadContinue(40, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationAddDeadContinue(40, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable: 100 is not a block id. ASSERT_FALSE(TransformationAddDeadContinue(100, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable: 10 is not in a loop. ASSERT_FALSE(TransformationAddDeadContinue(10, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable: 15 does not branch unconditionally to a single successor. ASSERT_FALSE(TransformationAddDeadContinue(15, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable: 13 is not in a loop and has no successor. ASSERT_FALSE(TransformationAddDeadContinue(13, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable: 14 is the loop continue target, so it's not OK to jump to // the loop continue from there. ASSERT_FALSE(TransformationAddDeadContinue(14, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // These are the transformations we will apply. auto transformation1 = TransformationAddDeadContinue(11, true, {}); auto transformation2 = TransformationAddDeadContinue(12, false, {}); auto transformation3 = TransformationAddDeadContinue(40, true, {}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -365,19 +371,24 @@ TEST(TransformationAddDeadContinueTest, LoopNest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); std::vector good = {6, 7, 18, 20, 34, 40, 45, 46, 47, 56, 57}; std::vector bad = {5, 8, 9, 19, 21, 22, 33, 41, 58, 59, 60}; for (uint32_t from_block : bad) { ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } for (uint32_t from_block : good) { const TransformationAddDeadContinue transformation(from_block, true, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } std::string after_transformation = R"( @@ -600,19 +611,24 @@ TEST(TransformationAddDeadConditionalTest, LoopInContinueConstruct) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); std::vector good = {32, 33, 46, 52, 101}; std::vector bad = {5, 34, 36, 35, 47, 49, 48}; for (uint32_t from_block : bad) { ASSERT_FALSE(TransformationAddDeadContinue(from_block, false, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } for (uint32_t from_block : good) { const TransformationAddDeadContinue transformation(from_block, false, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } std::string after_transformation = R"( @@ -806,6 +822,9 @@ TEST(TransformationAddDeadContinueTest, PhiInstructions) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); std::vector bad = {5, 19, 20, 23, 31, 32, 33, 70}; @@ -813,24 +832,28 @@ TEST(TransformationAddDeadContinueTest, PhiInstructions) { for (uint32_t from_block : bad) { ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } auto transformation1 = TransformationAddDeadContinue(29, true, {13, 21}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); auto transformation2 = TransformationAddDeadContinue(30, true, {22, 46}); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); // 75 already has the continue block as a successor, so we should not provide // phi ids. auto transformationBad = TransformationAddDeadContinue(75, true, {27, 46}); - ASSERT_FALSE(transformationBad.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformationBad.IsApplicable(context.get(), transformation_context)); auto transformation3 = TransformationAddDeadContinue(75, true, {}); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); std::string after_transformation = R"( OpCapability Shader @@ -974,26 +997,33 @@ TEST(TransformationAddDeadContinueTest, RespectDominanceRules1) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // This transformation is not applicable because the dead continue from the // loop body prevents the definition of %23 later in the loop body from // dominating its use in the loop's continue target. auto bad_transformation = TransformationAddDeadContinue(13, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); auto good_transformation_1 = TransformationAddDeadContinue(7, false, {}); - ASSERT_TRUE(good_transformation_1.IsApplicable(context.get(), fact_manager)); - good_transformation_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(good_transformation_1.IsApplicable(context.get(), + transformation_context)); + good_transformation_1.Apply(context.get(), &transformation_context); auto good_transformation_2 = TransformationAddDeadContinue(22, false, {}); - ASSERT_TRUE(good_transformation_2.IsApplicable(context.get(), fact_manager)); - good_transformation_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(good_transformation_2.IsApplicable(context.get(), + transformation_context)); + good_transformation_2.Apply(context.get(), &transformation_context); // This transformation is OK, because the definition of %21 in the loop body // is only used in an OpPhi in the loop's continue target. auto good_transformation_3 = TransformationAddDeadContinue(6, false, {11}); - ASSERT_TRUE(good_transformation_3.IsApplicable(context.get(), fact_manager)); - good_transformation_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(good_transformation_3.IsApplicable(context.get(), + transformation_context)); + good_transformation_3.Apply(context.get(), &transformation_context); std::string after_transformations = R"( OpCapability Shader @@ -1083,11 +1113,15 @@ TEST(TransformationAddDeadContinueTest, RespectDominanceRules2) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // This transformation would shortcut the part of the loop body that defines // an id used after the loop. auto bad_transformation = TransformationAddDeadContinue(100, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadContinueTest, RespectDominanceRules3) { @@ -1131,11 +1165,15 @@ TEST(TransformationAddDeadContinueTest, RespectDominanceRules3) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // This transformation would shortcut the part of the loop body that defines // an id used after the loop. auto bad_transformation = TransformationAddDeadContinue(100, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadContinueTest, Miscellaneous1) { @@ -1270,11 +1308,15 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous1) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // This transformation would shortcut the part of the loop body that defines // an id used in the continue target. auto bad_transformation = TransformationAddDeadContinue(165, false, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadContinueTest, Miscellaneous2) { @@ -1336,11 +1378,15 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous2) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // This transformation would introduce a branch from a continue target to // itself. auto bad_transformation = TransformationAddDeadContinue(1554, true, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadContinueTest, Miscellaneous3) { @@ -1394,13 +1440,17 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous3) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadContinue(299, false, {}); // The continue edge would connect %299 to the previously-unreachable %236, // making %299 dominate %236, and breaking the rule that block ordering must // respect dominance. - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadContinueTest, Miscellaneous4) { @@ -1454,13 +1504,17 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous4) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadContinue(10, false, {}); // The continue edge would connect %10 to the previously-unreachable %13, // making %10 dominate %13, and breaking the rule that block ordering must // respect dominance. - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadContinueTest, Miscellaneous5) { @@ -1506,12 +1560,16 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous5) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadContinue(110, true, {}); // The continue edge would lead to the use of %200 in block %101 no longer // being dominated by its definition in block %111. - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddDeadContinueTest, Miscellaneous6) { @@ -1551,10 +1609,14 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous6) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_transformation = TransformationAddDeadContinue(10, true, {}); - ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_add_function_test.cpp b/test/fuzz/transformation_add_function_test.cpp index aed12dc06a..0d1f2c44e3 100644 --- a/test/fuzz/transformation_add_function_test.cpp +++ b/test/fuzz/transformation_add_function_test.cpp @@ -59,13 +59,14 @@ std::vector GetInstructionsForFunction( } // Returns true if and only if every pointer parameter and variable associated -// with |function_id| in |context| is known by |fact_manager| to be irrelevant, -// with the exception of |loop_limiter_id|, which must not be irrelevant. (It -// can be 0 if no loop limiter is expected, and 0 should not be deemed -// irrelevant). +// with |function_id| in |context| is known by |transformation_context| to be +// irrelevant, with the exception of |loop_limiter_id|, which must not be +// irrelevant. (It can be 0 if no loop limiter is expected, and 0 should not be +// deemed irrelevant). bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - opt::IRContext* context, const FactManager& fact_manager, - uint32_t function_id, uint32_t loop_limiter_id) { + opt::IRContext* context, + const TransformationContext& transformation_context, uint32_t function_id, + uint32_t loop_limiter_id) { // Look at all the functions until the function of interest is found. for (auto& function : *context->module()) { if (function.result_id() != function_id) { @@ -73,15 +74,16 @@ bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( } // Check that the parameters are all irrelevant. bool found_non_irrelevant_parameter = false; - function.ForEachParam( - [context, &fact_manager, - &found_non_irrelevant_parameter](opt::Instruction* inst) { - if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == - SpvOpTypePointer && - !fact_manager.PointeeValueIsIrrelevant(inst->result_id())) { - found_non_irrelevant_parameter = true; - } - }); + function.ForEachParam([context, &transformation_context, + &found_non_irrelevant_parameter]( + opt::Instruction* inst) { + if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == + SpvOpTypePointer && + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + inst->result_id())) { + found_non_irrelevant_parameter = true; + } + }); if (found_non_irrelevant_parameter) { // A non-irrelevant parameter was found. return false; @@ -96,7 +98,8 @@ bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( // The variable should be irrelevant if and only if it is not the loop // limiter. if ((inst.result_id() == loop_limiter_id) == - fact_manager.PointeeValueIsIrrelevant(inst.result_id())) { + transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + inst.result_id())) { return false; } } @@ -142,6 +145,9 @@ TEST(TransformationAddFunctionTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationAddFunction transformation1(std::vector( {MakeInstructionMessage( @@ -212,8 +218,9 @@ TEST(TransformationAddFunctionTest, BasicTest) { {{SPV_OPERAND_TYPE_ID, {39}}}), MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation1 = R"( @@ -278,12 +285,12 @@ TEST(TransformationAddFunctionTest, BasicTest) { OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation1, context.get())); - ASSERT_TRUE(fact_manager.BlockIsDead(14)); - ASSERT_TRUE(fact_manager.BlockIsDead(21)); - ASSERT_TRUE(fact_manager.BlockIsDead(22)); - ASSERT_TRUE(fact_manager.BlockIsDead(23)); - ASSERT_TRUE(fact_manager.BlockIsDead(24)); - ASSERT_TRUE(fact_manager.BlockIsDead(25)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(14)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(21)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(22)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(23)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(24)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(25)); TransformationAddFunction transformation2(std::vector( {MakeInstructionMessage( @@ -332,8 +339,9 @@ TEST(TransformationAddFunctionTest, BasicTest) { MakeInstructionMessage(SpvOpReturn, 0, 0, {}), MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation2 = R"( @@ -414,7 +422,7 @@ TEST(TransformationAddFunctionTest, BasicTest) { OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation2, context.get())); - ASSERT_TRUE(fact_manager.BlockIsDead(16)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(16)); } TEST(TransformationAddFunctionTest, InapplicableTransformations) { @@ -486,11 +494,14 @@ TEST(TransformationAddFunctionTest, InapplicableTransformations) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // No instructions ASSERT_FALSE( TransformationAddFunction(std::vector({})) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No function begin ASSERT_FALSE( @@ -499,7 +510,7 @@ TEST(TransformationAddFunctionTest, InapplicableTransformations) { {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}), MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}), MakeInstructionMessage(SpvOpLabel, 0, 14, {})})) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No OpLabel ASSERT_FALSE( @@ -512,7 +523,7 @@ TEST(TransformationAddFunctionTest, InapplicableTransformations) { MakeInstructionMessage(SpvOpReturnValue, 0, 0, {{SPV_OPERAND_TYPE_ID, {39}}}), MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Abrupt end of instructions ASSERT_FALSE(TransformationAddFunction( @@ -521,7 +532,7 @@ TEST(TransformationAddFunctionTest, InapplicableTransformations) { {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, {SPV_OPERAND_TYPE_ID, {10}}})})) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No function end ASSERT_FALSE( @@ -534,7 +545,7 @@ TEST(TransformationAddFunctionTest, InapplicableTransformations) { MakeInstructionMessage(SpvOpLabel, 0, 14, {}), MakeInstructionMessage(SpvOpReturnValue, 0, 0, {{SPV_OPERAND_TYPE_ID, {39}}})})) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddFunctionTest, LoopLimiters) { @@ -622,20 +633,27 @@ TEST(TransformationAddFunctionTest, LoopLimiters) { FactManager fact_manager1; FactManager fact_manager2; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context1(&fact_manager1, + validator_options); + TransformationContext transformation_context2(&fact_manager2, + validator_options); const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); - add_dead_function.Apply(context1.get(), &fact_manager1); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + add_dead_function.Apply(context1.get(), &transformation_context1); ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. - ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(30)); + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(30)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context1.get(), fact_manager1, 30, 0)); + context1.get(), transformation_context1, 30, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -711,16 +729,16 @@ TEST(TransformationAddFunctionTest, LoopLimiters) { TransformationAddFunction add_livesafe_function(instructions, 100, 10, loop_limiters, 0, {}); - ASSERT_TRUE( - add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); - add_livesafe_function.Apply(context2.get(), &fact_manager2); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + add_livesafe_function.Apply(context2.get(), &transformation_context2); ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. - ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(30)); + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(30)); // All variables/parameters in the function should be deemed irrelevant, // except the loop limiter. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context2.get(), fact_manager2, 30, 100)); + context2.get(), transformation_context2, 30, 100)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -837,20 +855,27 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { FactManager fact_manager1; FactManager fact_manager2; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context1(&fact_manager1, + validator_options); + TransformationContext transformation_context2(&fact_manager2, + validator_options); const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); - add_dead_function.Apply(context1.get(), &fact_manager1); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + add_dead_function.Apply(context1.get(), &transformation_context1); ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. - ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(10)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context1.get(), fact_manager1, 10, 0)); + context1.get(), transformation_context1, 10, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -887,15 +912,15 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, {}); - ASSERT_TRUE( - add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); - add_livesafe_function.Apply(context2.get(), &fact_manager2); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + add_livesafe_function.Apply(context2.get(), &transformation_context2); ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. - ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context2.get(), fact_manager2, 10, 0)); + context2.get(), transformation_context2, 10, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -985,20 +1010,27 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { FactManager fact_manager1; FactManager fact_manager2; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context1(&fact_manager1, + validator_options); + TransformationContext transformation_context2(&fact_manager2, + validator_options); const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); - add_dead_function.Apply(context1.get(), &fact_manager1); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + add_dead_function.Apply(context1.get(), &transformation_context1); ASSERT_TRUE(IsValid(env, context1.get())); // The added function should not be deemed livesafe. - ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(10)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context1.get(), fact_manager1, 10, 0)); + context1.get(), transformation_context1, 10, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -1036,15 +1068,15 @@ TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, {}); - ASSERT_TRUE( - add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); - add_livesafe_function.Apply(context2.get(), &fact_manager2); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + add_livesafe_function.Apply(context2.get(), &transformation_context2); ASSERT_TRUE(IsValid(env, context2.get())); // The added function should indeed be deemed livesafe. - ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context2.get(), fact_manager2, 10, 0)); + context2.get(), transformation_context2, 10, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -1265,20 +1297,27 @@ TEST(TransformationAddFunctionTest, ClampedAccessChains) { FactManager fact_manager1; FactManager fact_manager2; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context1(&fact_manager1, + validator_options); + TransformationContext transformation_context2(&fact_manager2, + validator_options); const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); - add_dead_function.Apply(context1.get(), &fact_manager1); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + add_dead_function.Apply(context1.get(), &transformation_context1); ASSERT_TRUE(IsValid(env, context1.get())); // The function should not be deemed livesafe - ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(12)); + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(12)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context1.get(), fact_manager1, 12, 0)); + context1.get(), transformation_context1, 12, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -1409,15 +1448,15 @@ TEST(TransformationAddFunctionTest, ClampedAccessChains) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, access_chain_clamping_info); - ASSERT_TRUE( - add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); - add_livesafe_function.Apply(context2.get(), &fact_manager2); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + add_livesafe_function.Apply(context2.get(), &transformation_context2); ASSERT_TRUE(IsValid(env, context2.get())); // The function should be deemed livesafe - ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(12)); + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(12)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context2.get(), fact_manager2, 12, 0)); + context2.get(), transformation_context2, 12, 0)); std::string added_as_livesafe_code = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -1585,23 +1624,29 @@ TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { FactManager fact_manager1; FactManager fact_manager2; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context1(&fact_manager1, + validator_options); + TransformationContext transformation_context2(&fact_manager2, + validator_options); // Mark function 6 as livesafe. - fact_manager2.AddFactFunctionIsLivesafe(6); + transformation_context2.GetFactManager()->AddFactFunctionIsLivesafe(6); const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); - add_dead_function.Apply(context1.get(), &fact_manager1); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + add_dead_function.Apply(context1.get(), &transformation_context1); ASSERT_TRUE(IsValid(env, context1.get())); // The function should not be deemed livesafe - ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); + ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context1.get(), fact_manager1, 8, 0)); + context1.get(), transformation_context1, 8, 0)); std::string added_as_live_or_dead_code = R"( OpCapability Shader @@ -1630,15 +1675,15 @@ TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, {}); - ASSERT_TRUE( - add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); - add_livesafe_function.Apply(context2.get(), &fact_manager2); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + add_livesafe_function.Apply(context2.get(), &transformation_context2); ASSERT_TRUE(IsValid(env, context2.get())); // The function should be deemed livesafe - ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(8)); + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(8)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context2.get(), fact_manager2, 8, 0)); + context2.get(), transformation_context2, 8, 0)); ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get())); } @@ -1679,20 +1724,26 @@ TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { FactManager fact_manager1; FactManager fact_manager2; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context1(&fact_manager1, + validator_options); + TransformationContext transformation_context2(&fact_manager2, + validator_options); const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context1.get())); TransformationAddFunction add_dead_function(instructions); - ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); - add_dead_function.Apply(context1.get(), &fact_manager1); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + add_dead_function.Apply(context1.get(), &transformation_context1); ASSERT_TRUE(IsValid(env, context1.get())); // The function should not be deemed livesafe - ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); + ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8)); // All variables/parameters in the function should be deemed irrelevant. ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( - context1.get(), fact_manager1, 8, 0)); + context1.get(), transformation_context1, 8, 0)); std::string added_as_dead_code = R"( OpCapability Shader @@ -1721,8 +1772,8 @@ TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, {}); - ASSERT_FALSE( - add_livesafe_function.IsApplicable(context2.get(), fact_manager2)); + ASSERT_FALSE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); } TEST(TransformationAddFunctionTest, @@ -1804,6 +1855,9 @@ TEST(TransformationAddFunctionTest, const auto consumer = nullptr; FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); @@ -1821,8 +1875,9 @@ TEST(TransformationAddFunctionTest, loop_limiter_info.set_logical_op_id(105); TransformationAddFunction add_livesafe_function(instructions, 100, 32, {loop_limiter_info}, 0, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); - add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + add_livesafe_function.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected = R"( OpCapability Shader @@ -1958,6 +2013,9 @@ TEST(TransformationAddFunctionTest, const auto consumer = nullptr; FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); @@ -1975,8 +2033,9 @@ TEST(TransformationAddFunctionTest, loop_limiter_info.set_logical_op_id(105); TransformationAddFunction add_livesafe_function(instructions, 100, 32, {loop_limiter_info}, 0, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); - add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + add_livesafe_function.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected = R"( OpCapability Shader @@ -2110,6 +2169,9 @@ TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) { const auto consumer = nullptr; FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); @@ -2127,8 +2189,9 @@ TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) { loop_limiter_info.set_logical_op_id(105); TransformationAddFunction add_livesafe_function(instructions, 100, 32, {loop_limiter_info}, 0, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); - add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + add_livesafe_function.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected = R"( OpCapability Shader @@ -2254,6 +2317,9 @@ TEST(TransformationAddFunctionTest, InfiniteLoop1) { const auto consumer = nullptr; FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); @@ -2271,8 +2337,9 @@ TEST(TransformationAddFunctionTest, InfiniteLoop1) { loop_limiter_info.set_logical_op_id(105); TransformationAddFunction add_livesafe_function(instructions, 100, 32, {loop_limiter_info}, 0, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); - add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + add_livesafe_function.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected = R"( OpCapability Shader @@ -2408,6 +2475,9 @@ TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) { const auto consumer = nullptr; FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); @@ -2425,8 +2495,9 @@ TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) { loop_limiter_info.set_logical_op_id(105); TransformationAddFunction add_livesafe_function(instructions, 100, 32, {loop_limiter_info}, 0, {}); - ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager)); - add_livesafe_function.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + add_livesafe_function.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected = R"( OpCapability Shader @@ -2570,6 +2641,9 @@ TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) { const auto consumer = nullptr; FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); @@ -2590,15 +2664,17 @@ TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) { {loop_limiter_info}, 0, {}); // The loop limiter info is not good enough; it does not include ids to patch // up the OpPhi at the loop merge. - ASSERT_FALSE(no_op_phi_data.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + no_op_phi_data.IsApplicable(context.get(), transformation_context)); // Add a phi id for the new edge from the loop back edge block to the loop // merge. loop_limiter_info.add_phi_id(28); TransformationAddFunction with_op_phi_data(instructions, 100, 28, {loop_limiter_info}, 0, {}); - ASSERT_TRUE(with_op_phi_data.IsApplicable(context.get(), fact_manager)); - with_op_phi_data.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + with_op_phi_data.IsApplicable(context.get(), transformation_context)); + with_op_phi_data.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected = R"( OpCapability Shader @@ -2758,6 +2834,9 @@ TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) { const auto consumer = nullptr; FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); @@ -2776,8 +2855,9 @@ TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) { TransformationAddFunction transformation(instructions, 100, 28, {loop_limiter_info}, 0, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_global_undef_test.cpp b/test/fuzz/transformation_add_global_undef_test.cpp index c14f7e9169..8c06db02ca 100644 --- a/test/fuzz/transformation_add_global_undef_test.cpp +++ b/test/fuzz/transformation_add_global_undef_test.cpp @@ -47,17 +47,20 @@ TEST(TransformationAddGlobalUndefTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id already in use - ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable( + context.get(), transformation_context)); // %1 is not a type - ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable( + context.get(), transformation_context)); // %3 is a function type - ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable( + context.get(), transformation_context)); TransformationAddGlobalUndef transformations[] = { // %100 = OpUndef %6 @@ -79,8 +82,9 @@ TEST(TransformationAddGlobalUndefTest, BasicTest) { TransformationAddGlobalUndef(105, 11)}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp index 619f068fd2..9b8faa4c42 100644 --- a/test/fuzz/transformation_add_global_variable_test.cpp +++ b/test/fuzz/transformation_add_global_variable_test.cpp @@ -60,49 +60,52 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id already in use ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %1 is not a type ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0, false) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %7 is not a pointer type ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %9 does not have Private storage class ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0, false) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %15 does not have Private storage class ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %10 is a pointer to float, while %16 is an int constant ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16, false) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %10 is a Private pointer to float, while %15 is a variable with type // Uniform float pointer ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %12 is a Private pointer to int, while %10 is a variable with type // Private float pointer ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10, false) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK // since the initializer's type should be the *pointee* type. ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // This would work in principle, but logical addressing does not allow // a pointer to a pointer. ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14, false) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private @@ -124,15 +127,22 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { TransformationAddGlobalVariable(105, 19, 22, false)}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(104)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(103)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(105)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105)); ASSERT_TRUE(IsValid(env, context.get())); @@ -223,6 +233,9 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private @@ -235,12 +248,16 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { TransformationAddGlobalVariable(102, 19, 21, true)}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( diff --git a/test/fuzz/transformation_add_local_variable_test.cpp b/test/fuzz/transformation_add_local_variable_test.cpp index fd7047f871..e989b33ed4 100644 --- a/test/fuzz/transformation_add_local_variable_test.cpp +++ b/test/fuzz/transformation_add_local_variable_test.cpp @@ -79,66 +79,81 @@ TEST(TransformationAddLocalVariableTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // A few cases of inapplicable transformations: // Id 4 is already in use ASSERT_FALSE(TransformationAddLocalVariable(4, 50, 4, 51, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Type mismatch between initializer and pointer ASSERT_FALSE(TransformationAddLocalVariable(105, 46, 4, 51, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Id 5 is not a function ASSERT_FALSE(TransformationAddLocalVariable(105, 50, 5, 51, true) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %105 = OpVariable %50 Function %51 { TransformationAddLocalVariable transformation(105, 50, 4, 51, true); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } // %104 = OpVariable %41 Function %46 { TransformationAddLocalVariable transformation(104, 41, 4, 46, false); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } // %103 = OpVariable %35 Function %38 { TransformationAddLocalVariable transformation(103, 35, 4, 38, true); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } // %102 = OpVariable %31 Function %33 { TransformationAddLocalVariable transformation(102, 31, 4, 33, false); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } // %101 = OpVariable %19 Function %29 { TransformationAddLocalVariable transformation(101, 19, 4, 29, true); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } // %100 = OpVariable %8 Function %12 { TransformationAddLocalVariable transformation(100, 8, 4, 12, false); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(101)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(102)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(103)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(104)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(105)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105)); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_no_contraction_decoration_test.cpp b/test/fuzz/transformation_add_no_contraction_decoration_test.cpp index b1a87ead37..46841a5243 100644 --- a/test/fuzz/transformation_add_no_contraction_decoration_test.cpp +++ b/test/fuzz/transformation_add_no_contraction_decoration_test.cpp @@ -94,23 +94,27 @@ TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Invalid: 200 is not an id ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // Invalid: 17 is a block id ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // Invalid: 24 is not arithmetic ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // 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( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } diff --git a/test/fuzz/transformation_add_type_array_test.cpp b/test/fuzz/transformation_add_type_array_test.cpp index 2bcbe73e03..4392f99f30 100644 --- a/test/fuzz/transformation_add_type_array_test.cpp +++ b/test/fuzz/transformation_add_type_array_test.cpp @@ -54,37 +54,40 @@ TEST(TransformationAddTypeArrayTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id already in use ASSERT_FALSE(TransformationAddTypeArray(4, 10, 16).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // %1 is not a type ASSERT_FALSE(TransformationAddTypeArray(100, 1, 16) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %3 is a function type ASSERT_FALSE(TransformationAddTypeArray(100, 3, 16) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %2 is not a constant ASSERT_FALSE(TransformationAddTypeArray(100, 11, 2) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %18 is not an integer ASSERT_FALSE(TransformationAddTypeArray(100, 11, 18) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %13 is signed 0 ASSERT_FALSE(TransformationAddTypeArray(100, 11, 13) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %14 is negative ASSERT_FALSE(TransformationAddTypeArray(100, 11, 14) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %17 is unsigned 0 ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationAddTypeArray transformations[] = { // %100 = OpTypeArray %10 %16 @@ -94,8 +97,9 @@ TEST(TransformationAddTypeArrayTest, BasicTest) { TransformationAddTypeArray(101, 7, 12)}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/transformation_add_type_boolean_test.cpp b/test/fuzz/transformation_add_type_boolean_test.cpp index 9975953bac..60eabd9d3e 100644 --- a/test/fuzz/transformation_add_type_boolean_test.cpp +++ b/test/fuzz/transformation_add_type_boolean_test.cpp @@ -42,19 +42,23 @@ TEST(TransformationAddTypeBooleanTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Not applicable because id 1 is already in use. - ASSERT_FALSE(TransformationAddTypeBoolean(1).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddTypeBoolean(1).IsApplicable( + context.get(), transformation_context)); auto add_type_bool = TransformationAddTypeBoolean(100); - ASSERT_TRUE(add_type_bool.IsApplicable(context.get(), fact_manager)); - add_type_bool.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_type_bool.IsApplicable(context.get(), transformation_context)); + add_type_bool.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Not applicable as we already have this type now. - ASSERT_FALSE(TransformationAddTypeBoolean(101).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddTypeBoolean(101).IsApplicable( + context.get(), transformation_context)); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_type_float_test.cpp b/test/fuzz/transformation_add_type_float_test.cpp index 67408da5a0..7d17266709 100644 --- a/test/fuzz/transformation_add_type_float_test.cpp +++ b/test/fuzz/transformation_add_type_float_test.cpp @@ -42,19 +42,23 @@ TEST(TransformationAddTypeFloatTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Not applicable because id 1 is already in use. - ASSERT_FALSE(TransformationAddTypeFloat(1, 32).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddTypeFloat(1, 32).IsApplicable( + context.get(), transformation_context)); auto add_type_float_32 = TransformationAddTypeFloat(100, 32); - ASSERT_TRUE(add_type_float_32.IsApplicable(context.get(), fact_manager)); - add_type_float_32.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + add_type_float_32.IsApplicable(context.get(), transformation_context)); + add_type_float_32.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Not applicable as we already have this type now. - ASSERT_FALSE(TransformationAddTypeFloat(101, 32).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddTypeFloat(101, 32).IsApplicable( + context.get(), transformation_context)); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_type_function_test.cpp b/test/fuzz/transformation_add_type_function_test.cpp index 46bd436bbf..1557bb81d3 100644 --- a/test/fuzz/transformation_add_type_function_test.cpp +++ b/test/fuzz/transformation_add_type_function_test.cpp @@ -59,21 +59,24 @@ TEST(TransformationAddTypeFunctionTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id already in use ASSERT_FALSE(TransformationAddTypeFunction(4, 12, {12, 16, 14}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %1 is not a type ASSERT_FALSE(TransformationAddTypeFunction(100, 1, {12, 16, 14}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %18 is a function type ASSERT_FALSE(TransformationAddTypeFunction(100, 12, {18}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // A function of this signature already exists ASSERT_FALSE(TransformationAddTypeFunction(100, 17, {14, 16}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationAddTypeFunction transformations[] = { // %100 = OpTypeFunction %12 %12 %16 %14 @@ -86,8 +89,9 @@ TEST(TransformationAddTypeFunctionTest, BasicTest) { TransformationAddTypeFunction(102, 17, {200, 16})}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp index c6f884c203..63b17c22af 100644 --- a/test/fuzz/transformation_add_type_int_test.cpp +++ b/test/fuzz/transformation_add_type_int_test.cpp @@ -42,10 +42,13 @@ TEST(TransformationAddTypeIntTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Not applicable because id 1 is already in use. ASSERT_FALSE(TransformationAddTypeInt(1, 32, false) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto add_type_signed_int_32 = TransformationAddTypeInt(100, 32, true); auto add_type_unsigned_int_32 = TransformationAddTypeInt(101, 32, false); @@ -53,20 +56,21 @@ TEST(TransformationAddTypeIntTest, BasicTest) { auto add_type_unsigned_int_32_again = TransformationAddTypeInt(103, 32, false); - ASSERT_TRUE(add_type_signed_int_32.IsApplicable(context.get(), fact_manager)); - add_type_signed_int_32.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_type_signed_int_32.IsApplicable(context.get(), + transformation_context)); + add_type_signed_int_32.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE( - add_type_unsigned_int_32.IsApplicable(context.get(), fact_manager)); - add_type_unsigned_int_32.Apply(context.get(), &fact_manager); + ASSERT_TRUE(add_type_unsigned_int_32.IsApplicable(context.get(), + transformation_context)); + add_type_unsigned_int_32.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Not applicable as we already have these types now. - ASSERT_FALSE( - add_type_signed_int_32_again.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE( - add_type_unsigned_int_32_again.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(add_type_signed_int_32_again.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(add_type_unsigned_int_32_again.IsApplicable( + context.get(), transformation_context)); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_type_matrix_test.cpp b/test/fuzz/transformation_add_type_matrix_test.cpp index 84f27e9653..e925012ef3 100644 --- a/test/fuzz/transformation_add_type_matrix_test.cpp +++ b/test/fuzz/transformation_add_type_matrix_test.cpp @@ -47,17 +47,20 @@ TEST(TransformationAddTypeMatrixTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id already in use - ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable( + context.get(), transformation_context)); // %1 is not a type ASSERT_FALSE(TransformationAddTypeMatrix(100, 1, 2).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // %11 is not a floating-point vector ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationAddTypeMatrix transformations[] = { // %100 = OpTypeMatrix %8 2 @@ -88,8 +91,9 @@ TEST(TransformationAddTypeMatrixTest, BasicTest) { TransformationAddTypeMatrix(108, 10, 4)}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/transformation_add_type_pointer_test.cpp b/test/fuzz/transformation_add_type_pointer_test.cpp index e36707f54a..35303e4145 100644 --- a/test/fuzz/transformation_add_type_pointer_test.cpp +++ b/test/fuzz/transformation_add_type_pointer_test.cpp @@ -97,6 +97,9 @@ TEST(TransformationAddTypePointerTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto bad_type_id_does_not_exist = TransformationAddTypePointer(100, SpvStorageClassFunction, 101); @@ -122,12 +125,12 @@ TEST(TransformationAddTypePointerTest, BasicTest) { auto good_new_private_pointer_to_uniform_pointer_to_vec2 = TransformationAddTypePointer(108, SpvStorageClassPrivate, 107); - ASSERT_FALSE( - bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE( - bad_type_id_is_not_type.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE( - bad_result_id_is_not_fresh.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_type_id_does_not_exist.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(bad_type_id_is_not_type.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(bad_result_id_is_not_fresh.IsApplicable(context.get(), + transformation_context)); for (auto& transformation : {good_new_private_pointer_to_t, good_new_uniform_pointer_to_t, @@ -136,8 +139,9 @@ TEST(TransformationAddTypePointerTest, BasicTest) { good_new_private_pointer_to_private_pointer_to_float, good_new_uniform_pointer_to_vec2, good_new_private_pointer_to_uniform_pointer_to_vec2}) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } diff --git a/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp index ae68c9a256..06f78cd33d 100644 --- a/test/fuzz/transformation_add_type_struct_test.cpp +++ b/test/fuzz/transformation_add_type_struct_test.cpp @@ -47,17 +47,20 @@ TEST(TransformationAddTypeStructTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id already in use - ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable( + context.get(), transformation_context)); // %1 is not a type ASSERT_FALSE(TransformationAddTypeStruct(100, {1}).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // %3 is a function type ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); TransformationAddTypeStruct transformations[] = { // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11 @@ -73,8 +76,9 @@ TEST(TransformationAddTypeStructTest, BasicTest) { TransformationAddTypeStruct(103, {6, 6})}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/transformation_add_type_vector_test.cpp b/test/fuzz/transformation_add_type_vector_test.cpp index 6ac4498ee6..f1252a307b 100644 --- a/test/fuzz/transformation_add_type_vector_test.cpp +++ b/test/fuzz/transformation_add_type_vector_test.cpp @@ -45,13 +45,16 @@ TEST(TransformationAddTypeVectorTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Id already in use - ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable( + context.get(), transformation_context)); // %1 is not a type ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); TransformationAddTypeVector transformations[] = { // %100 = OpTypeVector %6 2 @@ -67,8 +70,9 @@ TEST(TransformationAddTypeVectorTest, BasicTest) { TransformationAddTypeVector(103, 9, 2)}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); diff --git a/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp index d303368ad4..e9d7610f91 100644 --- a/test/fuzz/transformation_composite_construct_test.cpp +++ b/test/fuzz/transformation_composite_construct_test.cpp @@ -129,6 +129,9 @@ TEST(TransformationCompositeConstructTest, ConstructArrays) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Make a vec2[3] TransformationCompositeConstruct make_vec2_array_length_3( @@ -138,17 +141,17 @@ TEST(TransformationCompositeConstructTest, ConstructArrays) { TransformationCompositeConstruct make_vec2_array_length_3_bad( 37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 200); - ASSERT_TRUE( - make_vec2_array_length_3.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE( - make_vec2_array_length_3_bad.IsApplicable(context.get(), fact_manager)); - make_vec2_array_length_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable( + context.get(), transformation_context)); + make_vec2_array_length_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2}), context.get())); // Make a float[2] @@ -157,15 +160,15 @@ TEST(TransformationCompositeConstructTest, ConstructArrays) { // Bad: %41 does not have type float TransformationCompositeConstruct make_float_array_length_2_bad( 9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201); - ASSERT_TRUE( - make_float_array_length_2.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE( - make_float_array_length_2_bad.IsApplicable(context.get(), fact_manager)); - make_float_array_length_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_float_array_length_2.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_float_array_length_2_bad.IsApplicable( + context.get(), transformation_context)); + make_float_array_length_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1}), context.get())); // Make a bool[3] @@ -176,17 +179,17 @@ TEST(TransformationCompositeConstructTest, ConstructArrays) { TransformationCompositeConstruct make_bool_array_length_3_bad( 47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0), 202); - ASSERT_TRUE( - make_bool_array_length_3.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE( - make_bool_array_length_3_bad.IsApplicable(context.get(), fact_manager)); - make_bool_array_length_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_bool_array_length_3.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_bool_array_length_3_bad.IsApplicable( + context.get(), transformation_context)); + make_bool_array_length_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2}), context.get())); // make a uvec3[2][2] @@ -195,17 +198,17 @@ TEST(TransformationCompositeConstructTest, ConstructArrays) { // Bad: Skip count 100 is too large. TransformationCompositeConstruct make_uvec3_array_length_2_2_bad( 58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203); - ASSERT_TRUE( - make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(), - fact_manager)); - make_uvec3_array_length_2_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_uvec3_array_length_2_2.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable( + context.get(), transformation_context)); + make_uvec3_array_length_2_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}), - MakeDataDescriptor(203, {1}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(203, {1}), + context.get())); std::string after_transformation = R"( OpCapability Shader @@ -393,6 +396,9 @@ TEST(TransformationCompositeConstructTest, ConstructMatrices) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // make a mat3x4 TransformationCompositeConstruct make_mat34( @@ -400,15 +406,16 @@ TEST(TransformationCompositeConstructTest, ConstructMatrices) { // Bad: %35 is mat4x3, not mat3x4. TransformationCompositeConstruct make_mat34_bad( 35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); - ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager)); - make_mat34.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_mat34_bad.IsApplicable(context.get(), transformation_context)); + make_mat34.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2}), context.get())); // make a mat4x3 @@ -417,19 +424,20 @@ TEST(TransformationCompositeConstructTest, ConstructMatrices) { // Bad: %25 does not match the matrix's column type. TransformationCompositeConstruct make_mat43_bad( 35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201); - ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager)); - make_mat43.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_mat43.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_mat43_bad.IsApplicable(context.get(), transformation_context)); + make_mat43.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(100, {}), - MakeDataDescriptor(201, {3}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(201, {3}), + context.get())); std::string after_transformation = R"( OpCapability Shader @@ -602,6 +610,9 @@ TEST(TransformationCompositeConstructTest, ConstructStructs) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // make an Inner TransformationCompositeConstruct make_inner( @@ -609,13 +620,14 @@ TEST(TransformationCompositeConstructTest, ConstructStructs) { // Bad: Too few fields to make the struct. TransformationCompositeConstruct make_inner_bad( 9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200); - ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager)); - make_inner.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_inner_bad.IsApplicable(context.get(), transformation_context)); + make_inner.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1}), context.get())); // make an Outer @@ -626,16 +638,17 @@ TEST(TransformationCompositeConstructTest, ConstructStructs) { TransformationCompositeConstruct make_outer_bad( 33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201); - ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager)); - make_outer.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_outer.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_outer_bad.IsApplicable(context.get(), transformation_context)); + make_outer.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(200, {}), - MakeDataDescriptor(201, {1}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(200, {}), MakeDataDescriptor(201, {1}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2}), context.get())); std::string after_transformation = R"( @@ -922,19 +935,23 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationCompositeConstruct make_vec2( 7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200); // Bad: not enough data for a vec2 TransformationCompositeConstruct make_vec2_bad( 7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200); - ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager)); - make_vec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_vec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_vec2_bad.IsApplicable(context.get(), transformation_context)); + make_vec2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1}), context.get())); TransformationCompositeConstruct make_vec3( @@ -944,17 +961,18 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { TransformationCompositeConstruct make_vec3_bad( 25, {12, 32, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201); - ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager)); - make_vec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_vec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_vec3_bad.IsApplicable(context.get(), transformation_context)); + make_vec3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}), - MakeDataDescriptor(201, {0}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {1}), - MakeDataDescriptor(201, {1}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {0}), MakeDataDescriptor(201, {0}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {1}), MakeDataDescriptor(201, {1}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2}), context.get())); TransformationCompositeConstruct make_vec4( @@ -964,17 +982,18 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { TransformationCompositeConstruct make_vec4_bad( 44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 202); - ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager)); - make_vec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_vec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_vec4_bad.IsApplicable(context.get(), transformation_context)); + make_vec4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3}), context.get())); TransformationCompositeConstruct make_ivec2( @@ -982,16 +1001,17 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { // Bad: if 128 is not available at the instruction that defines 128 TransformationCompositeConstruct make_ivec2_bad( 51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203); - ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager)); - make_ivec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_ivec2_bad.IsApplicable(context.get(), transformation_context)); + make_ivec2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(126, {}), - MakeDataDescriptor(203, {0}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(120, {}), - MakeDataDescriptor(203, {1}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(126, {}), MakeDataDescriptor(203, {0}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(120, {}), MakeDataDescriptor(203, {1}), + context.get())); TransformationCompositeConstruct make_ivec3( 114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0), @@ -1000,16 +1020,17 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { TransformationCompositeConstruct make_ivec3_bad( 114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0), 204); - ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager)); - make_ivec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_ivec3_bad.IsApplicable(context.get(), transformation_context)); + make_ivec3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}), - MakeDataDescriptor(204, {1}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(204, {1}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2}), context.get())); TransformationCompositeConstruct make_ivec4( @@ -1019,33 +1040,35 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { TransformationCompositeConstruct make_ivec4_bad( 86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0), 205); - ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager)); - make_ivec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_ivec4_bad.IsApplicable(context.get(), transformation_context)); + make_ivec4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}), - MakeDataDescriptor(205, {1}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}), - MakeDataDescriptor(205, {2}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(117, {}), - MakeDataDescriptor(205, {3}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {1}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {2}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {3}), + context.get())); TransformationCompositeConstruct make_uvec2( 86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206); TransformationCompositeConstruct make_uvec2_bad( 86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206); - ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager)); - make_uvec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_uvec2_bad.IsApplicable(context.get(), transformation_context)); + make_uvec2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1}), context.get())); TransformationCompositeConstruct make_uvec3( @@ -1053,17 +1076,18 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { // Bad because 1300 is not an id TransformationCompositeConstruct make_uvec3_bad( 59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207); - ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager)); - make_uvec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_uvec3_bad.IsApplicable(context.get(), transformation_context)); + make_uvec3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}), - MakeDataDescriptor(207, {2}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(136, {}), MakeDataDescriptor(207, {2}), + context.get())); TransformationCompositeConstruct make_uvec4( 131, {14, 18, 136, 136}, @@ -1072,20 +1096,21 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { TransformationCompositeConstruct make_uvec4_bad( 86, {14, 18, 136, 136}, MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208); - ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager)); - make_uvec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_uvec4_bad.IsApplicable(context.get(), transformation_context)); + make_uvec4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}), - MakeDataDescriptor(208, {2}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(136, {}), - MakeDataDescriptor(208, {3}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {2}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {3}), + context.get())); TransformationCompositeConstruct make_bvec2( 102, @@ -1102,14 +1127,15 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { 41, }, MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209); - ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager)); - make_bvec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_bvec2_bad.IsApplicable(context.get(), transformation_context)); + make_bvec2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(111, {}), - MakeDataDescriptor(209, {0}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(111, {}), MakeDataDescriptor(209, {0}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1}), context.get())); TransformationCompositeConstruct make_bvec3( @@ -1117,17 +1143,18 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { // Bad because there are too many components for a bvec3 TransformationCompositeConstruct make_bvec3_bad( 93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210); - ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager)); - make_bvec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_bvec3_bad.IsApplicable(context.get(), transformation_context)); + make_bvec3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}), - MakeDataDescriptor(210, {0}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}), - MakeDataDescriptor(210, {1}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {0}), MakeDataDescriptor(210, {0}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {1}), MakeDataDescriptor(210, {1}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2}), context.get())); TransformationCompositeConstruct make_bvec4( @@ -1135,22 +1162,23 @@ TEST(TransformationCompositeConstructTest, ConstructVectors) { // Bad because 21 is a type, not a result id TransformationCompositeConstruct make_bvec4_bad( 70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211); - ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager)); - make_bvec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_bvec4_bad.IsApplicable(context.get(), transformation_context)); + make_bvec4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}), - MakeDataDescriptor(211, {0}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}), - MakeDataDescriptor(211, {1}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {0}), - MakeDataDescriptor(211, {2}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(108, {1}), - MakeDataDescriptor(211, {3}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {0}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {1}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {2}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {3}), + context.get())); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp index 5cc211513a..d57b62c973 100644 --- a/test/fuzz/transformation_composite_extract_test.cpp +++ b/test/fuzz/transformation_composite_extract_test.cpp @@ -96,100 +96,109 @@ TEST(TransformationCompositeExtractTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Instruction does not exist. ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Id for composite is not a composite. ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 27, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Composite does not dominate instruction being inserted before. ASSERT_FALSE( TransformationCompositeExtract( MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Too many indices for extraction from struct composite. ASSERT_FALSE( TransformationCompositeExtract( MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Too many indices for extraction from struct composite. ASSERT_FALSE( TransformationCompositeExtract( MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Out of bounds index for extraction from struct composite. ASSERT_FALSE( TransformationCompositeExtract( MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Result id already used. ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationCompositeExtract transformation_1( MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2}); - ASSERT_TRUE(transformation_1.IsApplicable(context.get(), fact_manager)); - transformation_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation_1.IsApplicable(context.get(), transformation_context)); + transformation_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); TransformationCompositeExtract transformation_2( MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2}); - ASSERT_TRUE(transformation_2.IsApplicable(context.get(), fact_manager)); - transformation_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation_2.IsApplicable(context.get(), transformation_context)); + transformation_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); TransformationCompositeExtract transformation_3( MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0}); - ASSERT_TRUE(transformation_3.IsApplicable(context.get(), fact_manager)); - transformation_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation_3.IsApplicable(context.get(), transformation_context)); + transformation_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); TransformationCompositeExtract transformation_4( MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0}); - ASSERT_TRUE(transformation_4.IsApplicable(context.get(), fact_manager)); - transformation_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation_4.IsApplicable(context.get(), transformation_context)); + transformation_4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); TransformationCompositeExtract transformation_5( MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2}); - ASSERT_TRUE(transformation_5.IsApplicable(context.get(), fact_manager)); - transformation_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation_5.IsApplicable(context.get(), transformation_context)); + transformation_5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); TransformationCompositeExtract transformation_6( MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1}); - ASSERT_TRUE(transformation_6.IsApplicable(context.get(), fact_manager)); - transformation_6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation_6.IsApplicable(context.get(), transformation_context)); + transformation_6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}), - MakeDataDescriptor(100, {2}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(202, {}), - MakeDataDescriptor(104, {0, 2}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(203, {}), - MakeDataDescriptor(104, {0}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(204, {}), - MakeDataDescriptor(101, {0}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(205, {}), - MakeDataDescriptor(102, {2}), - context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(206, {}), - MakeDataDescriptor(103, {1}), - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(100, {2}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(202, {}), MakeDataDescriptor(104, {0, 2}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(203, {}), MakeDataDescriptor(104, {0}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(204, {}), MakeDataDescriptor(101, {0}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(205, {}), MakeDataDescriptor(102, {2}), + context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(206, {}), MakeDataDescriptor(103, {1}), + context.get())); std::string after_transformation = R"( OpCapability Shader @@ -348,49 +357,52 @@ TEST(TransformationCompositeExtractTest, IllegalInsertionPoints) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Cannot insert before the OpVariables of a function. ASSERT_FALSE( TransformationCompositeExtract( MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationCompositeExtract( MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationCompositeExtract( MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK to insert right after the OpVariables. ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Cannot insert before the OpPhis of a block. ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK to insert after the OpPhis. ASSERT_TRUE( TransformationCompositeExtract( MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Cannot insert before OpLoopMerge ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(33, SpvOpBranchConditional, 0), 200, 14, {3}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Cannot insert before OpSelectionMerge ASSERT_FALSE(TransformationCompositeExtract( MakeInstructionDescriptor(21, SpvOpBranchConditional, 0), 200, 14, {2}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp index b85f75b458..fa5a5b12ee 100644 --- a/test/fuzz/transformation_copy_object_test.cpp +++ b/test/fuzz/transformation_copy_object_test.cpp @@ -51,77 +51,95 @@ TEST(TransformationCopyObjectTest, CopyBooleanConstants) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - ASSERT_EQ(0, - fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()).size()); + ASSERT_EQ(0, transformation_context.GetFactManager() + ->GetIdsForWhichSynonymsAreKnown(context.get()) + .size()); { TransformationCopyObject copy_true( 7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100); - ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager)); - copy_true.Apply(context.get(), &fact_manager); + ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context)); + copy_true.Apply(context.get(), &transformation_context); std::vector ids_for_which_synonyms_are_known = - fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()); + transformation_context.GetFactManager()->GetIdsForWhichSynonymsAreKnown( + context.get()); ASSERT_EQ(2, ids_for_which_synonyms_are_known.size()); ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), ids_for_which_synonyms_are_known.end(), 7) != ids_for_which_synonyms_are_known.end()); - ASSERT_EQ(2, fact_manager.GetSynonymsForId(7, context.get()).size()); + ASSERT_EQ(2, transformation_context.GetFactManager() + ->GetSynonymsForId(7, context.get()) + .size()); protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}), - descriptor_100, context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(7, {}), descriptor_100, context.get())); } { TransformationCopyObject copy_false( 8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101); - ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager)); - copy_false.Apply(context.get(), &fact_manager); + ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context)); + copy_false.Apply(context.get(), &transformation_context); std::vector ids_for_which_synonyms_are_known = - fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()); + transformation_context.GetFactManager()->GetIdsForWhichSynonymsAreKnown( + context.get()); ASSERT_EQ(4, ids_for_which_synonyms_are_known.size()); ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), ids_for_which_synonyms_are_known.end(), 8) != ids_for_which_synonyms_are_known.end()); - ASSERT_EQ(2, fact_manager.GetSynonymsForId(8, context.get()).size()); + ASSERT_EQ(2, transformation_context.GetFactManager() + ->GetSynonymsForId(8, context.get()) + .size()); protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(8, {}), - descriptor_101, context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(8, {}), descriptor_101, context.get())); } { TransformationCopyObject copy_false_again( 101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102); - ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager)); - copy_false_again.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + copy_false_again.IsApplicable(context.get(), transformation_context)); + copy_false_again.Apply(context.get(), &transformation_context); std::vector ids_for_which_synonyms_are_known = - fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()); + transformation_context.GetFactManager()->GetIdsForWhichSynonymsAreKnown( + context.get()); ASSERT_EQ(5, ids_for_which_synonyms_are_known.size()); ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), ids_for_which_synonyms_are_known.end(), 101) != ids_for_which_synonyms_are_known.end()); - ASSERT_EQ(3, fact_manager.GetSynonymsForId(101, context.get()).size()); + ASSERT_EQ(3, transformation_context.GetFactManager() + ->GetSynonymsForId(101, context.get()) + .size()); protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(101, {}), - descriptor_102, context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(101, {}), descriptor_102, context.get())); } { TransformationCopyObject copy_true_again( 7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103); - ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager)); - copy_true_again.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + copy_true_again.IsApplicable(context.get(), transformation_context)); + copy_true_again.Apply(context.get(), &transformation_context); std::vector ids_for_which_synonyms_are_known = - fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()); + transformation_context.GetFactManager()->GetIdsForWhichSynonymsAreKnown( + context.get()); ASSERT_EQ(6, ids_for_which_synonyms_are_known.size()); ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), ids_for_which_synonyms_are_known.end(), 7) != ids_for_which_synonyms_are_known.end()); - ASSERT_EQ(3, fact_manager.GetSynonymsForId(7, context.get()).size()); + ASSERT_EQ(3, transformation_context.GetFactManager() + ->GetSynonymsForId(7, context.get()) + .size()); protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}), - descriptor_103, context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(7, {}), descriptor_103, context.get())); } std::string after_transformation = R"( @@ -340,116 +358,119 @@ TEST(TransformationCopyObjectTest, CheckIllegalCases) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Inapplicable because %18 is decorated. ASSERT_FALSE(TransformationCopyObject( 18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because %77 is decorated. ASSERT_FALSE(TransformationCopyObject( 77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because %80 is decorated. ASSERT_FALSE(TransformationCopyObject( 80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because %84 is not available at the requested point ASSERT_FALSE( TransformationCopyObject( 84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Fine because %84 is available at the requested point ASSERT_TRUE( TransformationCopyObject( 84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because id %9 is already in use ASSERT_FALSE( TransformationCopyObject( 84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because the requested point does not exist ASSERT_FALSE(TransformationCopyObject( 84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because %9 is not in a function ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because the insert point is right before, or inside, a chunk // of OpPhis ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK, because the insert point is just after a chunk of OpPhis. ASSERT_TRUE(TransformationCopyObject( 9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because the insert point is right after an OpSelectionMerge ASSERT_FALSE( TransformationCopyObject( 9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK, because the insert point is right before the OpSelectionMerge ASSERT_TRUE(TransformationCopyObject( 9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because the insert point is right after an OpSelectionMerge ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK, because the insert point is right before the OpSelectionMerge ASSERT_TRUE(TransformationCopyObject( 9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because the insert point is right after an OpLoopMerge ASSERT_FALSE( TransformationCopyObject( 9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK, because the insert point is right before the OpLoopMerge ASSERT_TRUE(TransformationCopyObject( 9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because id %300 does not exist ASSERT_FALSE(TransformationCopyObject( 300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Inapplicable because the following instruction is OpVariable ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK, because this is just past the group of OpVariable instructions. ASSERT_TRUE(TransformationCopyObject( 9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationCopyObjectTest, MiscellaneousCopies) { @@ -515,6 +536,9 @@ TEST(TransformationCopyObjectTest, MiscellaneousCopies) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); std::vector transformations = { TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0), @@ -533,8 +557,9 @@ TEST(TransformationCopyObjectTest, MiscellaneousCopies) { 17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)}; for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); } ASSERT_TRUE(IsValid(env, context.get())); @@ -614,16 +639,19 @@ TEST(TransformationCopyObjectTest, DoNotCopyNullOrUndefPointers) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Illegal to copy null. ASSERT_FALSE(TransformationCopyObject( 8, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Illegal to copy an OpUndef of pointer type. ASSERT_FALSE(TransformationCopyObject( 9, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) { @@ -655,7 +683,11 @@ TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - fact_manager.AddFactValueOfPointeeIsIrrelevant(8); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8); TransformationCopyObject transformation1( 8, MakeInstructionDescriptor(9, SpvOpReturn, 0), 100); @@ -664,18 +696,26 @@ TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) { TransformationCopyObject transformation3( 100, MakeInstructionDescriptor(9, SpvOpReturn, 0), 102); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); - - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(8)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(100)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(102)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(9)); - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(101)); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); + + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); } } // namespace diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp index 81d849b465..be9024e0a0 100644 --- a/test/fuzz/transformation_equation_instruction_test.cpp +++ b/test/fuzz/transformation_equation_instruction_test.cpp @@ -48,6 +48,9 @@ TEST(TransformationEquationInstructionTest, SignedNegate) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); @@ -55,58 +58,60 @@ TEST(TransformationEquationInstructionTest, SignedNegate) { // Bad: id already in use. ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: identified instruction does not exist. ASSERT_FALSE( TransformationEquationInstruction( 14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: id 100 does not exist ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: id 20 is an OpUndef ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: id 30 is not available right before its definition ASSERT_FALSE(TransformationEquationInstruction( 14, SpvOpSNegate, {30}, MakeInstructionDescriptor(30, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: too many arguments to OpSNegate. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: 40 is a type id. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: wrong type of argument to OpSNegate. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, SpvOpSNegate, {7}, return_instruction); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 15, SpvOpSNegate, {14}, return_instruction); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get())); std::string after_transformation = R"( @@ -161,6 +166,9 @@ TEST(TransformationEquationInstructionTest, LogicalNot) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); @@ -168,31 +176,33 @@ TEST(TransformationEquationInstructionTest, LogicalNot) { // Bad: too few arguments to OpLogicalNot. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: 6 is a type id. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: wrong type of argument to OpLogicalNot. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, SpvOpLogicalNot, {7}, return_instruction); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 15, SpvOpLogicalNot, {14}, return_instruction); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get())); std::string after_transformation = R"( @@ -248,6 +258,9 @@ TEST(TransformationEquationInstructionTest, AddSubNegate1) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); @@ -255,58 +268,63 @@ TEST(TransformationEquationInstructionTest, AddSubNegate1) { // Bad: too many arguments to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: boolean argument to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: type as argument to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: arguments of mismatched widths ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: arguments of mismatched widths ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15}, return_instruction) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, SpvOpIAdd, {15, 16}, return_instruction); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 19, SpvOpISub, {14, 16}, return_instruction); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}), context.get())); auto transformation3 = TransformationEquationInstruction( 20, SpvOpISub, {14, 15}, return_instruction); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get())); auto transformation4 = TransformationEquationInstruction( 22, SpvOpISub, {16, 14}, return_instruction); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation5 = TransformationEquationInstruction( 24, SpvOpSNegate, {22}, return_instruction); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get())); std::string after_transformation = R"( @@ -364,68 +382,79 @@ TEST(TransformationEquationInstructionTest, AddSubNegate2) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); auto transformation1 = TransformationEquationInstruction( 14, SpvOpISub, {15, 16}, return_instruction); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 17, SpvOpIAdd, {14, 16}, return_instruction); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get())); auto transformation3 = TransformationEquationInstruction( 18, SpvOpIAdd, {16, 14}, return_instruction); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get())); auto transformation4 = TransformationEquationInstruction( 19, SpvOpISub, {14, 15}, return_instruction); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation5 = TransformationEquationInstruction( 20, SpvOpSNegate, {19}, return_instruction); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get())); auto transformation6 = TransformationEquationInstruction( 21, SpvOpISub, {14, 19}, return_instruction); - ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); - transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + transformation6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get())); auto transformation7 = TransformationEquationInstruction( 22, SpvOpISub, {14, 18}, return_instruction); - ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager)); - transformation7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + transformation7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation8 = TransformationEquationInstruction( 23, SpvOpSNegate, {22}, return_instruction); - ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager)); - transformation8.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation8.IsApplicable(context.get(), transformation_context)); + transformation8.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.IsSynonymous( + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get())); std::string after_transformation = R"( diff --git a/test/fuzz/transformation_function_call_test.cpp b/test/fuzz/transformation_function_call_test.cpp index 9bd971e230..d7305f8705 100644 --- a/test/fuzz/transformation_function_call_test.cpp +++ b/test/fuzz/transformation_function_call_test.cpp @@ -134,24 +134,36 @@ TEST(TransformationFunctionCallTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - fact_manager.AddFactBlockIsDead(59); - fact_manager.AddFactBlockIsDead(11); - fact_manager.AddFactBlockIsDead(18); - fact_manager.AddFactBlockIsDead(25); - fact_manager.AddFactBlockIsDead(96); - fact_manager.AddFactBlockIsDead(205); - fact_manager.AddFactFunctionIsLivesafe(21); - fact_manager.AddFactFunctionIsLivesafe(200); - fact_manager.AddFactValueOfPointeeIsIrrelevant(71); - fact_manager.AddFactValueOfPointeeIsIrrelevant(72); - fact_manager.AddFactValueOfPointeeIsIrrelevant(19); - fact_manager.AddFactValueOfPointeeIsIrrelevant(20); - fact_manager.AddFactValueOfPointeeIsIrrelevant(23); - fact_manager.AddFactValueOfPointeeIsIrrelevant(44); - fact_manager.AddFactValueOfPointeeIsIrrelevant(46); - fact_manager.AddFactValueOfPointeeIsIrrelevant(51); - fact_manager.AddFactValueOfPointeeIsIrrelevant(52); + transformation_context.GetFactManager()->AddFactBlockIsDead(59); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); + transformation_context.GetFactManager()->AddFactBlockIsDead(18); + transformation_context.GetFactManager()->AddFactBlockIsDead(25); + transformation_context.GetFactManager()->AddFactBlockIsDead(96); + transformation_context.GetFactManager()->AddFactBlockIsDead(205); + transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(21); + transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(200); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 71); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 72); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 19); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 20); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 23); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 44); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 46); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 51); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 52); // Livesafe functions with argument types: 21(7, 13), 200(7, 13) // Non-livesafe functions with argument types: 4(), 10(7), 17(7, 13), 24(7) @@ -164,127 +176,133 @@ TEST(TransformationFunctionCallTest, BasicTest) { ASSERT_FALSE( TransformationFunctionCall(100, 21, {71, 72, 71}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Too few arguments ASSERT_FALSE(TransformationFunctionCall( 100, 21, {71}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Arguments are the wrong way around (types do not match) ASSERT_FALSE( TransformationFunctionCall(100, 21, {72, 71}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // 21 is not an appropriate argument ASSERT_FALSE( TransformationFunctionCall(100, 21, {21, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // 300 does not exist ASSERT_FALSE( TransformationFunctionCall(100, 21, {300, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // 71 is not a function ASSERT_FALSE( TransformationFunctionCall(100, 71, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // 500 does not exist ASSERT_FALSE( TransformationFunctionCall(100, 500, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Id is not fresh ASSERT_FALSE( TransformationFunctionCall(21, 21, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Access chain as pointer parameter ASSERT_FALSE( TransformationFunctionCall(100, 21, {98, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Copied object as pointer parameter ASSERT_FALSE( TransformationFunctionCall(100, 21, {99, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Non-livesafe called from original live block ASSERT_FALSE( TransformationFunctionCall( 100, 10, {71}, MakeInstructionDescriptor(99, SpvOpSelectionMerge, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Non-livesafe called from livesafe function ASSERT_FALSE( TransformationFunctionCall( 100, 10, {19}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Livesafe function called with pointer to non-arbitrary local variable ASSERT_FALSE( TransformationFunctionCall( 100, 21, {61, 72}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Direct recursion ASSERT_FALSE(TransformationFunctionCall( 100, 4, {}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Indirect recursion ASSERT_FALSE(TransformationFunctionCall( 100, 24, {9}, MakeInstructionDescriptor(96, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Parameter 23 is not available at the call site ASSERT_FALSE( TransformationFunctionCall(104, 10, {23}, MakeInstructionDescriptor(205, SpvOpBranch, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Good transformations { // Livesafe called from dead block: fine TransformationFunctionCall transformation( 100, 21, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { // Livesafe called from original live block: fine TransformationFunctionCall transformation( 101, 21, {71, 72}, MakeInstructionDescriptor(98, SpvOpAccessChain, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { // Livesafe called from livesafe function: fine TransformationFunctionCall transformation( 102, 200, {19, 20}, MakeInstructionDescriptor(36, SpvOpLoad, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { // Dead called from dead block in injected function: fine TransformationFunctionCall transformation( 103, 10, {23}, MakeInstructionDescriptor(45, SpvOpLoad, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { // Non-livesafe called from dead block in livesafe function: OK TransformationFunctionCall transformation( 104, 10, {201}, MakeInstructionDescriptor(205, SpvOpBranch, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { // Livesafe called from dead block with non-arbitrary parameter TransformationFunctionCall transformation( 105, 21, {62, 65}, MakeInstructionDescriptor(59, SpvOpBranch, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -429,13 +447,16 @@ TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - fact_manager.AddFactBlockIsDead(11); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); // 4 is an entry point, so it is not legal for it to be the target of a call. ASSERT_FALSE(TransformationFunctionCall( 100, 4, {}, MakeInstructionDescriptor(11, SpvOpReturn, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp index 1f728ffcfa..18ca195e73 100644 --- a/test/fuzz/transformation_load_test.cpp +++ b/test/fuzz/transformation_load_test.cpp @@ -85,14 +85,22 @@ TEST(TransformationLoadTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - fact_manager.AddFactValueOfPointeeIsIrrelevant(27); - fact_manager.AddFactValueOfPointeeIsIrrelevant(11); - fact_manager.AddFactValueOfPointeeIsIrrelevant(46); - fact_manager.AddFactValueOfPointeeIsIrrelevant(16); - fact_manager.AddFactValueOfPointeeIsIrrelevant(52); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 27); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 11); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 46); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 16); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 52); - fact_manager.AddFactBlockIsDead(36); + transformation_context.GetFactManager()->AddFactBlockIsDead(36); // Variables with pointee types: // 52 - ptr_to(7) @@ -125,86 +133,90 @@ TEST(TransformationLoadTest, BasicTest) { // Bad: id is not fresh ASSERT_FALSE(TransformationLoad( 33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to load from 11 from outside its function ASSERT_FALSE(TransformationLoad( 100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available ASSERT_FALSE(TransformationLoad( 100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable ASSERT_FALSE(TransformationLoad( 100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist ASSERT_FALSE( TransformationLoad(100, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists but does not have a type ASSERT_FALSE(TransformationLoad( 100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists and has a type, but is not a pointer ASSERT_FALSE(TransformationLoad( 100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to load from null pointer ASSERT_FALSE(TransformationLoad( 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to load from undefined pointer ASSERT_FALSE(TransformationLoad( 100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: %40 is not available at the program point ASSERT_FALSE( TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: The described instruction does not exist ASSERT_FALSE(TransformationLoad( 100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); { TransformationLoad transformation( 100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { TransformationLoad transformation( 101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { TransformationLoad transformation( 102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { TransformationLoad transformation( 103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } diff --git a/test/fuzz/transformation_merge_blocks_test.cpp b/test/fuzz/transformation_merge_blocks_test.cpp index e2b4aa65a9..4500445bfb 100644 --- a/test/fuzz/transformation_merge_blocks_test.cpp +++ b/test/fuzz/transformation_merge_blocks_test.cpp @@ -45,11 +45,14 @@ TEST(TransformationMergeBlocksTest, BlockDoesNotExist) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - - ASSERT_FALSE( - TransformationMergeBlocks(3).IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE( - TransformationMergeBlocks(7).IsApplicable(context.get(), fact_manager)); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + ASSERT_FALSE(TransformationMergeBlocks(3).IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeBlocks(7).IsApplicable( + context.get(), transformation_context)); } TEST(TransformationMergeBlocksTest, DoNotMergeFirstBlockHasMultipleSuccessors) { @@ -84,9 +87,12 @@ TEST(TransformationMergeBlocksTest, DoNotMergeFirstBlockHasMultipleSuccessors) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - ASSERT_FALSE( - TransformationMergeBlocks(6).IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationMergeBlocks(6).IsApplicable( + context.get(), transformation_context)); } TEST(TransformationMergeBlocksTest, @@ -122,9 +128,12 @@ TEST(TransformationMergeBlocksTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - ASSERT_FALSE( - TransformationMergeBlocks(10).IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationMergeBlocks(10).IsApplicable( + context.get(), transformation_context)); } TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionMerge) { @@ -161,10 +170,14 @@ TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionMerge) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationMergeBlocks transformation(10); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -231,10 +244,14 @@ TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopMerge) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationMergeBlocks transformation(10); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -306,10 +323,14 @@ TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopContinue) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationMergeBlocks transformation(11); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -377,10 +398,14 @@ TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockStartsWithOpPhi) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationMergeBlocks transformation(6); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -454,12 +479,16 @@ TEST(TransformationMergeBlocksTest, BasicMerge) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); for (auto& transformation : {TransformationMergeBlocks(100), TransformationMergeBlocks(101), TransformationMergeBlocks(102), TransformationMergeBlocks(103)}) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -542,11 +571,15 @@ TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionHeader) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); for (auto& transformation : {TransformationMergeBlocks(101), TransformationMergeBlocks(100)}) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -629,10 +662,14 @@ TEST(TransformationMergeBlocksTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationMergeBlocks transformation(101); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( diff --git a/test/fuzz/transformation_move_block_down_test.cpp b/test/fuzz/transformation_move_block_down_test.cpp index 02761a2cf3..662e88c054 100644 --- a/test/fuzz/transformation_move_block_down_test.cpp +++ b/test/fuzz/transformation_move_block_down_test.cpp @@ -53,9 +53,13 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible1) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto transformation = TransformationMoveBlockDown(11); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationMoveBlockDownTest, NoMovePossible2) { @@ -90,9 +94,13 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible2) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto transformation = TransformationMoveBlockDown(5); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationMoveBlockDownTest, NoMovePossible3) { @@ -129,9 +137,13 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible3) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto transformation = TransformationMoveBlockDown(100); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationMoveBlockDownTest, NoMovePossible4) { @@ -172,9 +184,13 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible4) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto transformation = TransformationMoveBlockDown(12); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationMoveBlockDownTest, ManyMovesPossible) { @@ -277,6 +293,9 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) { BuildModule(env, consumer, before_transformation, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // The block ids are: 5 14 20 23 21 25 29 32 30 15 // We make a transformation to move each of them down, plus a transformation @@ -306,110 +325,130 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) { // 15 dominates nothing // Current ordering: 5 14 20 23 21 25 29 32 30 15 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); // Let's bubble 20 all the way down. - move_down_20.Apply(context.get(), &fact_manager); + move_down_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 23 20 21 25 29 32 30 15 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager)); - - move_down_20.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + move_down_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 23 21 20 25 29 32 30 15 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager)); - - move_down_20.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + move_down_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 23 21 25 20 29 32 30 15 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager)); - - move_down_20.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + move_down_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 23 21 25 29 20 32 30 15 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager)); - - move_down_20.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + move_down_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 23 21 25 29 32 20 30 15 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager)); - - move_down_20.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + move_down_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 23 21 25 29 32 30 20 15 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager)); - - move_down_20.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + move_down_20.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_bubbling_20_down = R"( @@ -485,63 +524,72 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) { ASSERT_TRUE(IsEqual(env, after_bubbling_20_down, context.get())); // Current ordering: 5 14 23 21 25 29 32 30 15 20 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager)); - - move_down_23.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + move_down_23.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 21 23 25 29 32 30 15 20 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager)); - - move_down_23.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + move_down_23.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 21 25 23 29 32 30 15 20 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager)); - - move_down_21.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + move_down_21.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Current ordering: 5 14 25 21 23 29 32 30 15 20 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager)); - - move_down_14.Apply(context.get(), &fact_manager); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + move_down_14.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_more_shuffling = R"( @@ -617,16 +665,18 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) { ASSERT_TRUE(IsEqual(env, after_more_shuffling, context.get())); // Final ordering: 5 25 14 21 23 29 32 30 15 20 - ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); } TEST(TransformationMoveBlockDownTest, DoNotMoveUnreachable) { @@ -660,9 +710,13 @@ TEST(TransformationMoveBlockDownTest, DoNotMoveUnreachable) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto transformation = TransformationMoveBlockDown(6); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index 40aaebc9a7..7beed85164 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -44,12 +44,16 @@ TEST(TransformationOutlineFunctionTest, TrivialOutline) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, 100, 101, 102, 103, /* not relevant */ 201, {}, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -105,11 +109,15 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, 100, 101, 102, 103, /* not relevant */ 201, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) { @@ -158,12 +166,16 @@ TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 13, /* not relevant */ 200, 100, 101, 102, 103, /* not relevant */ 201, {}, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -243,12 +255,16 @@ TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesUnusedIds) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 6, /* not relevant */ 200, 100, 101, 102, 103, /* not relevant */ 201, {}, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -317,11 +333,15 @@ TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesSingleUsedId) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 6, 99, 100, 101, 102, 103, 105, {}, {{9, 104}}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -412,12 +432,16 @@ TEST(TransformationOutlineFunctionTest, OutlineDiamondThatGeneratesSeveralIds) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( 6, 80, 100, 101, 102, 103, 104, 105, {}, {{15, 106}, {9, 107}, {7, 108}, {8, 109}}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -508,11 +532,15 @@ TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesASingleId) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104, 105, {{7, 106}}, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -582,11 +610,15 @@ TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAVariable) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104, 105, {{13, 106}}, {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -666,11 +698,15 @@ TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAParameter) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(11, 11, 100, 101, 102, 103, 104, 105, {{9, 106}}, {{14, 107}}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -752,10 +788,14 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104, 105, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) { @@ -798,11 +838,15 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, 100, 101, 102, 103, /* not relevant */ 201, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) { @@ -845,11 +889,15 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, 100, 101, 102, 103, /* not relevant */ 201, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -893,11 +941,15 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, 100, 101, 102, 103, /* not relevant */ 201, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -933,10 +985,14 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104, 105, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) { @@ -973,10 +1029,14 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(7, 8, 100, 101, 102, 103, 104, 105, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -1012,10 +1072,14 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104, 105, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -1053,10 +1117,14 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104, 105, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -1094,10 +1162,14 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation(8, 11, 100, 101, 102, 103, 104, 105, {}, {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) { @@ -1132,6 +1204,9 @@ TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 54, @@ -1145,8 +1220,9 @@ TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) { /*input_id_to_fresh_id*/ {{22, 206}}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1219,6 +1295,9 @@ TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnValue) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 9, @@ -1232,8 +1311,9 @@ TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnValue) { /*input_id_to_fresh_id*/ {{31, 206}}, /*output_id_to_fresh_id*/ {{32, 207}}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1310,6 +1390,9 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 54, @@ -1323,8 +1406,9 @@ TEST(TransformationOutlineFunctionTest, /*input_id_to_fresh_id*/ {{}}, /*output_id_to_fresh_id*/ {{6, 206}}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1396,6 +1480,9 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 54, @@ -1409,8 +1496,9 @@ TEST(TransformationOutlineFunctionTest, /*input_id_to_fresh_id*/ {}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1478,6 +1566,9 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatStartsWithOpPhi) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 21, @@ -1491,7 +1582,8 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatStartsWithOpPhi) { /*input_id_to_fresh_id*/ {{22, 207}}, /*output_id_to_fresh_id*/ {{23, 208}}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -1531,6 +1623,9 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 21, @@ -1544,7 +1639,8 @@ TEST(TransformationOutlineFunctionTest, /*input_id_to_fresh_id*/ {}, /*output_id_to_fresh_id*/ {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -1584,6 +1680,9 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 5, @@ -1597,7 +1696,8 @@ TEST(TransformationOutlineFunctionTest, /*input_id_to_fresh_id*/ {}, /*output_id_to_fresh_id*/ {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) { @@ -1640,6 +1740,9 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 13, @@ -1653,7 +1756,8 @@ TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) { /*input_id_to_fresh_id*/ {{12, 207}}, /*output_id_to_fresh_id*/ {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -1698,6 +1802,9 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 13, @@ -1711,7 +1818,8 @@ TEST(TransformationOutlineFunctionTest, /*input_id_to_fresh_id*/ {{20, 207}}, /*output_id_to_fresh_id*/ {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, @@ -1761,6 +1869,9 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 11, @@ -1774,8 +1885,9 @@ TEST(TransformationOutlineFunctionTest, /*input_id_to_fresh_id*/ {{9, 207}}, /*output_id_to_fresh_id*/ {{14, 208}}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -1913,9 +2025,15 @@ TEST(TransformationOutlineFunctionTest, OutlineLivesafe) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - fact_manager.AddFactFunctionIsLivesafe(30); - fact_manager.AddFactValueOfPointeeIsIrrelevant(200); - fact_manager.AddFactValueOfPointeeIsIrrelevant(201); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(30); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 200); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 201); TransformationOutlineFunction transformation( /*entry_block*/ 198, @@ -1929,24 +2047,31 @@ TEST(TransformationOutlineFunctionTest, OutlineLivesafe) { /*input_id_to_fresh_id*/ {{100, 407}, {200, 408}, {201, 409}}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // The original function should still be livesafe. - ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30)); + ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(30)); // The outlined function should be livesafe. - ASSERT_TRUE(fact_manager.FunctionIsLivesafe(402)); + ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(402)); // The variable and parameter that were originally irrelevant should still be. - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(200)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(201)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201)); // The loop limiter should still be non-irrelevant. - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(100)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); // The parameters for the original irrelevant variables should be irrelevant. - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(408)); - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(409)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(408)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(409)); // The parameter for the loop limiter should not be irrelevant. - ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(407)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(407)); std::string after_transformation = R"( OpCapability Shader @@ -2129,8 +2254,12 @@ TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks1) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u}) { - fact_manager.AddFactBlockIsDead(block_id); + transformation_context.GetFactManager()->AddFactBlockIsDead(block_id); } TransformationOutlineFunction transformation( @@ -2145,12 +2274,13 @@ TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks1) { /*input_id_to_fresh_id*/ {{9, 206}, {12, 207}, {21, 208}}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // All the original blocks, plus the new function entry block, should be dead. for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u, 203u}) { - ASSERT_TRUE(fact_manager.BlockIsDead(block_id)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id)); } } @@ -2208,8 +2338,12 @@ TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks2) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + for (uint32_t block_id : {32u, 34u, 35u}) { - fact_manager.AddFactBlockIsDead(block_id); + transformation_context.GetFactManager()->AddFactBlockIsDead(block_id); } TransformationOutlineFunction transformation( @@ -2224,15 +2358,17 @@ TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks2) { /*input_id_to_fresh_id*/ {{11, 206}}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // The blocks that were originally dead, but not others, should be dead. for (uint32_t block_id : {32u, 34u, 35u}) { - ASSERT_TRUE(fact_manager.BlockIsDead(block_id)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id)); } for (uint32_t block_id : {5u, 30u, 31u, 33u, 36u, 37u, 203u}) { - ASSERT_FALSE(fact_manager.BlockIsDead(block_id)); + ASSERT_FALSE( + transformation_context.GetFactManager()->BlockIsDead(block_id)); } } @@ -2287,8 +2423,13 @@ TEST(TransformationOutlineFunctionTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - fact_manager.AddFactValueOfPointeeIsIrrelevant(9); - fact_manager.AddFactValueOfPointeeIsIrrelevant(14); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(9); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 14); TransformationOutlineFunction transformation( /*entry_block*/ 50, @@ -2302,16 +2443,20 @@ TEST(TransformationOutlineFunctionTest, /*input_id_to_fresh_id*/ {{9, 206}, {10, 207}, {14, 208}, {20, 209}}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // The variables that were originally irrelevant, plus input parameters // corresponding to them, should be irrelevant. The rest should not be. for (uint32_t variable_id : {9u, 14u, 206u, 208u}) { - ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(variable_id)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + variable_id)); } for (uint32_t variable_id : {10u, 20u, 207u, 209u}) { - ASSERT_FALSE(fact_manager.BlockIsDead(variable_id)); + ASSERT_FALSE( + transformation_context.GetFactManager()->BlockIsDead(variable_id)); } } @@ -2423,6 +2568,9 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous1) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 150, @@ -2436,8 +2584,9 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous1) { /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}}, /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -2588,6 +2737,9 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous2) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 38, @@ -2601,7 +2753,8 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous2) { /*input_id_to_fresh_id*/ {}, /*output_id_to_fresh_id*/ {}); - ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationOutlineFunctionTest, Miscellaneous3) { @@ -2643,6 +2796,9 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous3) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 80, @@ -2656,8 +2812,9 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous3) { /*input_id_to_fresh_id*/ {}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( @@ -2732,6 +2889,9 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous4) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationOutlineFunction transformation( /*entry_block*/ 80, @@ -2745,8 +2905,9 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous4) { /*input_id_to_fresh_id*/ {{104, 307}}, /*output_id_to_fresh_id*/ {}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( diff --git a/test/fuzz/transformation_permute_function_parameters_test.cpp b/test/fuzz/transformation_permute_function_parameters_test.cpp index 1af4699cda..a4a7c00b78 100644 --- a/test/fuzz/transformation_permute_function_parameters_test.cpp +++ b/test/fuzz/transformation_permute_function_parameters_test.cpp @@ -200,52 +200,57 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Can't permute main function ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable( - context.get(), fact_manager)); + context.get(), transformation_context)); // Can't permute invalid instruction ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Permutation has too many values ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Permutation has too few values ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Permutation has invalid values ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Type id is not an OpTypeFunction instruction ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Type id has incorrect number of operands ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OpTypeFunction has operands out of order ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Successful transformations { // Function has two operands of the same type: // initial OpTypeFunction should be enough TransformationPermuteFunctionParameters transformation(12, 9, {1, 0}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { TransformationPermuteFunctionParameters transformation(28, 105, {1, 0}); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } diff --git a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp index 527a7b7d5a..b320308f61 100644 --- a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp +++ b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp @@ -163,6 +163,9 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); std::vector uses_of_true = { MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1), @@ -197,10 +200,10 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, #define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \ ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary( \ USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \ - .IsApplicable(context.get(), fact_manager)); \ + .IsApplicable(context.get(), transformation_context)); \ ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( \ USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID) \ - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); #define CHECK_TRANSFORMATION_APPLICABILITY(GT_OPCODES, LT_OPCODES, SMALL_ID, \ LARGE_ID) \ @@ -252,27 +255,27 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, // Target id is not fresh ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // LHS id does not exist ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // RHS id does not exist ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // LHS and RHS ids do not match type ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Opcode not appropriate ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 15, 17, SpvOpFDiv, 200) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto replace_true_with_double_comparison = TransformationReplaceBooleanConstantWithConstantBinary( @@ -287,21 +290,25 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, TransformationReplaceBooleanConstantWithConstantBinary( uses_of_false[1], 33, 31, SpvOpSLessThan, 103); - ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(context.get(), - fact_manager)); - replace_true_with_double_comparison.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable( + context.get(), transformation_context)); + replace_true_with_double_comparison.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable(context.get(), - fact_manager)); - replace_true_with_uint32_comparison.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable( + context.get(), transformation_context)); + replace_true_with_uint32_comparison.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable(context.get(), - fact_manager)); - replace_false_with_float_comparison.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable( + context.get(), transformation_context)); + replace_false_with_float_comparison.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable(context.get(), - fact_manager)); - replace_false_with_sint64_comparison.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable( + context.get(), transformation_context)); + replace_false_with_sint64_comparison.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after = R"( @@ -419,7 +426,7 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, // The transformation is not applicable because %200 is NaN. ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } if (std::numeric_limits::has_infinity) { double positive_infinity_double = std::numeric_limits::infinity(); @@ -436,7 +443,7 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, // transformation is restricted to only apply to finite values. ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } if (std::numeric_limits::has_infinity) { float positive_infinity_float = std::numeric_limits::infinity(); @@ -461,7 +468,7 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, // values. ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } } @@ -531,6 +538,9 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto use_of_true_in_if = MakeIdUseDescriptor( 13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0); @@ -542,12 +552,14 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary( use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101); - ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); - replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); - replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after = R"( @@ -642,12 +654,15 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto replacement = TransformationReplaceBooleanConstantWithConstantBinary( MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13, 15, SpvOpSLessThan, 100); - ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, @@ -681,12 +696,15 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( MakeIdUseDescriptor( 9, MakeInstructionDescriptor(50, SpvOpVariable, 0), 1), 13, 15, SpvOpSLessThan, 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp index 58d4a89509..8cbba465fe 100644 --- a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp +++ b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp @@ -22,7 +22,8 @@ namespace fuzz { namespace { bool AddFactHelper( - FactManager* fact_manager, opt::IRContext* context, uint32_t word, + TransformationContext* transformation_context, opt::IRContext* context, + uint32_t word, const protobufs::UniformBufferElementDescriptor& descriptor) { protobufs::FactConstantUniform constant_uniform_fact; constant_uniform_fact.add_constant_word(word); @@ -30,7 +31,7 @@ bool AddFactHelper( descriptor; protobufs::Fact fact; *fact.mutable_constant_uniform_fact() = constant_uniform_fact; - return fact_manager->AddFact(fact, context); + return transformation_context->GetFactManager()->AddFact(fact, context); } TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { @@ -104,6 +105,10 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + protobufs::UniformBufferElementDescriptor blockname_a = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_b = @@ -111,9 +116,12 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { protobufs::UniformBufferElementDescriptor blockname_c = MakeUniformBufferElementDescriptor(0, 0, {2}); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_a)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_b)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_c)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 1, blockname_a)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 2, blockname_b)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 3, blockname_c)); // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively. protobufs::IdUseDescriptor use_of_9_in_store = @@ -127,30 +135,30 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { auto transformation_use_of_9_in_store = TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_a, 100, 101); - ASSERT_TRUE(transformation_use_of_9_in_store.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_9_in_store.IsApplicable( + context.get(), transformation_context)); auto transformation_use_of_11_in_add = TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b, 102, 103); - ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable( + context.get(), transformation_context)); auto transformation_use_of_14_in_add = TransformationReplaceConstantWithUniform(use_of_14_in_add, blockname_c, 104, 105); - ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable( + context.get(), transformation_context)); // The transformations are not applicable if we change which uniforms are // applied to which constants. ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_b, 101, 102) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_c, 101, 102) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_14_in_add, blockname_a, 101, 102) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // The following transformations do not apply because the uniform descriptors // are not sensible. @@ -160,10 +168,10 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { MakeUniformBufferElementDescriptor(0, 0, {5}); ASSERT_FALSE(TransformationReplaceConstantWithUniform( use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform( use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // The following transformation does not apply because the id descriptor is // not sensible. @@ -171,18 +179,19 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0); ASSERT_FALSE(TransformationReplaceConstantWithUniform( nonsense_id_use_descriptor, blockname_a, 101, 102) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // The following transformations do not apply because the ids are not fresh. ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b, 15, 103) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b, 102, 15) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Apply the use of 9 in a store. - transformation_use_of_9_in_store.Apply(context.get(), &fact_manager); + transformation_use_of_9_in_store.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_replacing_use_of_9_in_store = R"( OpCapability Shader @@ -233,10 +242,10 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { )"; ASSERT_TRUE(IsEqual(env, after_replacing_use_of_9_in_store, context.get())); - ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable( + context.get(), transformation_context)); // Apply the use of 11 in an add. - transformation_use_of_11_in_add.Apply(context.get(), &fact_manager); + transformation_use_of_11_in_add.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_replacing_use_of_11_in_add = R"( OpCapability Shader @@ -289,10 +298,10 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { )"; ASSERT_TRUE(IsEqual(env, after_replacing_use_of_11_in_add, context.get())); - ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable( + context.get(), transformation_context)); // Apply the use of 15 in an add. - transformation_use_of_14_in_add.Apply(context.get(), &fact_manager); + transformation_use_of_14_in_add.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_replacing_use_of_14_in_add = R"( OpCapability Shader @@ -462,6 +471,10 @@ TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + protobufs::UniformBufferElementDescriptor blockname_1 = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_2 = @@ -471,10 +484,14 @@ TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) { protobufs::UniformBufferElementDescriptor blockname_4 = MakeUniformBufferElementDescriptor(0, 0, {1, 0, 1, 0}); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, blockname_1)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, blockname_2)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, blockname_3)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, blockname_4)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 1, blockname_1)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 2, blockname_2)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 3, blockname_3)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 4, blockname_4)); // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively. protobufs::IdUseDescriptor use_of_13_in_store = @@ -490,76 +507,78 @@ TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) { auto transformation_use_of_13_in_store = TransformationReplaceConstantWithUniform(use_of_13_in_store, blockname_1, 100, 101); - ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); auto transformation_use_of_15_in_add = TransformationReplaceConstantWithUniform(use_of_15_in_add, blockname_2, 102, 103); - ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); auto transformation_use_of_17_in_add = TransformationReplaceConstantWithUniform(use_of_17_in_add, blockname_3, 104, 105); - ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(), - fact_manager)); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); auto transformation_use_of_20_in_store = TransformationReplaceConstantWithUniform(use_of_20_in_store, blockname_4, 106, 107); - ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(), - fact_manager)); - - ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(), - fact_manager)); - - transformation_use_of_13_in_store.Apply(context.get(), &fact_manager); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + transformation_use_of_13_in_store.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(), - fact_manager)); - - transformation_use_of_15_in_add.Apply(context.get(), &fact_manager); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + transformation_use_of_15_in_add.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(), - fact_manager)); - ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(), - fact_manager)); - - transformation_use_of_17_in_add.Apply(context.get(), &fact_manager); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + transformation_use_of_17_in_add.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(), - fact_manager)); - ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(), - fact_manager)); - - transformation_use_of_20_in_store.Apply(context.get(), &fact_manager); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + transformation_use_of_20_in_store.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(), - fact_manager)); - ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(context.get(), - fact_manager)); - ASSERT_FALSE(transformation_use_of_20_in_store.IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); std::string after = R"( OpCapability Shader @@ -697,10 +716,15 @@ TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + protobufs::UniformBufferElementDescriptor blockname_0 = MakeUniformBufferElementDescriptor(0, 0, {0}); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_0)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 0, blockname_0)); // The constant id is 9 for 0. protobufs::IdUseDescriptor use_of_9_in_store = @@ -710,7 +734,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) { // type is present: ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_0, 100, 101) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) { @@ -770,12 +794,17 @@ TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + protobufs::UniformBufferElementDescriptor blockname_0 = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_9 = MakeUniformBufferElementDescriptor(0, 0, {1}); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 9, blockname_9)); // The constant id is 9 for 9. protobufs::IdUseDescriptor use_of_9_in_store = @@ -785,7 +814,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) { // index 1 required to index into the uniform buffer: ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_9, 100, 101) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, @@ -842,14 +871,18 @@ TEST(TransformationReplaceConstantWithUniformTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + protobufs::UniformBufferElementDescriptor blockname_3 = MakeUniformBufferElementDescriptor(0, 0, {0}); uint32_t float_data[1]; float temp = 3.0; memcpy(&float_data[0], &temp, sizeof(float)); - ASSERT_TRUE( - AddFactHelper(&fact_manager, context.get(), float_data[0], blockname_3)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_data[0], blockname_3)); // The constant id is 9 for 3.0. protobufs::IdUseDescriptor use_of_9_in_store = @@ -859,7 +892,7 @@ TEST(TransformationReplaceConstantWithUniformTest, // allow a constant index to be expressed: ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_3, 100, 101) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, @@ -928,13 +961,19 @@ TEST(TransformationReplaceConstantWithUniformTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + protobufs::UniformBufferElementDescriptor blockname_9 = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_10 = MakeUniformBufferElementDescriptor(0, 0, {1}); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 9, blockname_9)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 10, blockname_10)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 9, blockname_9)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 10, blockname_10)); // The constant ids for 9 and 10 are 9 and 11 respectively protobufs::IdUseDescriptor use_of_9_in_store = @@ -945,19 +984,19 @@ TEST(TransformationReplaceConstantWithUniformTest, // These are right: ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_9, 100, 101) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_11_in_store, blockname_10, 102, 103) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // These are wrong because the constants do not match the facts about // uniforms. ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_store, blockname_9, 100, 101) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_10, 102, 103) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { @@ -1141,6 +1180,9 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); const float float_array_values[5] = {1.0, 1.5, 1.75, 1.875, 1.9375}; uint32_t float_array_data[5]; @@ -1188,35 +1230,43 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { protobufs::UniformBufferElementDescriptor uniform_h_y = MakeUniformBufferElementDescriptor(0, 0, {2, 1}); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[0], - uniform_f_a_0)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[1], - uniform_f_a_1)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[2], - uniform_f_a_2)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[3], - uniform_f_a_3)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_array_data[4], - uniform_f_a_4)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_array_data[0], uniform_f_a_0)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_array_data[1], uniform_f_a_1)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_array_data[2], uniform_f_a_2)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_array_data[3], uniform_f_a_3)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_array_data[4], uniform_f_a_4)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 1, uniform_f_b_x)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 2, uniform_f_b_y)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 3, uniform_f_b_z)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 4, uniform_f_b_w)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 1, uniform_f_b_x)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 2, uniform_f_b_y)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 3, uniform_f_b_z)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 4, uniform_f_b_w)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[0], - uniform_f_c_x)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[1], - uniform_f_c_y)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_vector_data[2], - uniform_f_c_z)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_vector_data[0], uniform_f_c_x)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_vector_data[1], uniform_f_c_y)); + ASSERT_TRUE(AddFactHelper(&transformation_context, context.get(), + float_vector_data[2], uniform_f_c_z)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 42, uniform_f_d)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 42, uniform_f_d)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22, uniform_g)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 22, uniform_g)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 100, uniform_h_x)); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200, uniform_h_y)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 100, uniform_h_x)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 200, uniform_h_y)); std::vector transformations; @@ -1275,8 +1325,9 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { uniform_g, 218, 219)); for (auto& transformation : transformations) { - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -1480,16 +1531,21 @@ TEST(TransformationReplaceConstantWithUniformTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + protobufs::UniformBufferElementDescriptor blockname_a = MakeUniformBufferElementDescriptor(0, 0, {0}); - ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 0, blockname_a)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, context.get(), 0, blockname_a)); ASSERT_FALSE(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 50, MakeInstructionDescriptor(8, SpvOpVariable, 0), 1), blockname_a, 100, 101) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 41b6116678..d563afa75e 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -220,15 +220,19 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - SetUpIdSynonyms(&fact_manager, context.get()); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + SetUpIdSynonyms(transformation_context.GetFactManager(), context.get()); // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not // dominate %300. auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0), 202); - ASSERT_FALSE( - synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(synonym_does_not_dominate_use.IsApplicable( + context.get(), transformation_context)); // %202 cannot replace %15 as in-operand 2 of %301, since this is the OpPhi's // incoming value for block %72, and %202 does not dominate %72. @@ -237,22 +241,23 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0), 2), 202); - ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable( + context.get(), transformation_context)); // %200 is not a synonym for %84 auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor( 84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0), 200); - ASSERT_FALSE( - id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(id_in_use_is_not_synonymous.IsApplicable( + context.get(), transformation_context)); // %86 is not a synonym for anything (and in particular not for %74) auto id_has_no_synonyms = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2), 74); - ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + id_has_no_synonyms.IsApplicable(context.get(), transformation_context)); // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed auto synonym_use_is_in_synonym_definition = @@ -260,8 +265,8 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { MakeIdUseDescriptor( 84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0), 207); - ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(), - fact_manager)); + ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable( + context.get(), transformation_context)); // The id use descriptor does not lead to a use (%84 is not used in the // definition of %207) @@ -269,7 +274,8 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { MakeIdUseDescriptor( 84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0), 207); - ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), + transformation_context)); // This replacement would lead to an access chain into a struct using a // non-constant index. @@ -277,7 +283,8 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { MakeIdUseDescriptor( 12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1), 209); - ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + bad_access_chain.IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { @@ -288,23 +295,28 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - SetUpIdSynonyms(&fact_manager, context.get()); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + SetUpIdSynonyms(transformation_context.GetFactManager(), context.get()); auto global_constant_synonym = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1), 210); - ASSERT_TRUE( - global_constant_synonym.IsApplicable(context.get(), fact_manager)); - global_constant_synonym.Apply(context.get(), &fact_manager); + ASSERT_TRUE(global_constant_synonym.IsApplicable(context.get(), + transformation_context)); + global_constant_synonym.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor( 54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1), 204); - ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(), - fact_manager)); - replace_vector_access_chain_index.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable( + context.get(), transformation_context)); + replace_vector_access_chain_index.Apply(context.get(), + &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // This is an interesting case because it replaces something that is being @@ -313,22 +325,24 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { MakeIdUseDescriptor( 15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0), 201); - ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager)); - regular_replacement.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + regular_replacement.IsApplicable(context.get(), transformation_context)); + regular_replacement.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto regular_replacement2 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0), 203); - ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager)); - regular_replacement2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + regular_replacement2.IsApplicable(context.get(), transformation_context)); + regular_replacement2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto good_op_phi = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2), 205); - ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager)); - good_op_phi.Apply(context.get(), &fact_manager); + ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), transformation_context)); + good_op_phi.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); const std::string after_transformation = R"( @@ -504,17 +518,22 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - fact_manager.AddFact(MakeSynonymFact(10, 100), context.get()); - fact_manager.AddFact(MakeSynonymFact(8, 101), context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(10, 100), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(8, 101), + context.get()); // Replace %10 with %100 in: // %11 = OpLoad %6 %10 auto replacement1 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0), 100); - ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager)); - replacement1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context)); + replacement1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %8 with %101 in: @@ -522,8 +541,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { auto replacement2 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0), 101); - ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager)); - replacement2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context)); + replacement2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %8 with %101 in: @@ -531,8 +550,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { auto replacement3 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0), 101); - ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager)); - replacement3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context)); + replacement3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replace %10 with %100 in: @@ -540,8 +559,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { auto replacement4 = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0), 100); - ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager)); - replacement4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context)); + replacement4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); const std::string after_transformation = R"( @@ -633,8 +652,12 @@ TEST(TransformationReplaceIdWithSynonymTest, ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); - fact_manager.AddFact(MakeSynonymFact(14, 100), context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(14, 100), + context.get()); // Replace %14 with %100 in: // %16 = OpFunctionCall %2 %10 %14 @@ -642,7 +665,7 @@ TEST(TransformationReplaceIdWithSynonymTest, MakeIdUseDescriptor( 14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1), 100); - ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { @@ -795,22 +818,38 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Add synonym facts corresponding to the OpCopyObject operations that have // been applied to all constants in the module. - fact_manager.AddFact(MakeSynonymFact(16, 100), context.get()); - fact_manager.AddFact(MakeSynonymFact(21, 101), context.get()); - fact_manager.AddFact(MakeSynonymFact(17, 102), context.get()); - fact_manager.AddFact(MakeSynonymFact(57, 103), context.get()); - fact_manager.AddFact(MakeSynonymFact(18, 104), context.get()); - fact_manager.AddFact(MakeSynonymFact(40, 105), context.get()); - fact_manager.AddFact(MakeSynonymFact(32, 106), context.get()); - fact_manager.AddFact(MakeSynonymFact(43, 107), context.get()); - fact_manager.AddFact(MakeSynonymFact(55, 108), context.get()); - fact_manager.AddFact(MakeSynonymFact(8, 109), context.get()); - fact_manager.AddFact(MakeSynonymFact(47, 110), context.get()); - fact_manager.AddFact(MakeSynonymFact(28, 111), context.get()); - fact_manager.AddFact(MakeSynonymFact(45, 112), context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(16, 100), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(21, 101), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(17, 102), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(57, 103), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(18, 104), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(40, 105), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(32, 106), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(43, 107), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(55, 108), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(8, 109), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(47, 110), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(28, 111), + context.get()); + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(45, 112), + context.get()); // Replacements of the form %16 -> %100 @@ -821,7 +860,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1), 100); - ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement1.IsApplicable(context.get(), transformation_context)); // %39 = OpAccessChain %23 %37 *%16* // Corresponds to h.*f* @@ -830,7 +870,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1), 100); - ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement2.IsApplicable(context.get(), transformation_context)); // %41 = OpAccessChain %19 %37 %21 *%16* %21 // Corresponds to h.g.*a*[1] @@ -839,7 +880,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2), 100); - ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement3.IsApplicable(context.get(), transformation_context)); // %52 = OpAccessChain %23 %50 *%16* %16 // Corresponds to i[*0*].f @@ -848,8 +890,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1), 100); - ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager)); - replacement4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context)); + replacement4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // %52 = OpAccessChain %23 %50 %16 *%16* @@ -859,7 +901,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2), 100); - ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement5.IsApplicable(context.get(), transformation_context)); // %53 = OpAccessChain %19 %50 %21 %21 *%16* %16 // Corresponds to i[1].g.*a*[0] @@ -868,7 +911,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3), 100); - ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement6.IsApplicable(context.get(), transformation_context)); // %53 = OpAccessChain %19 %50 %21 %21 %16 *%16* // Corresponds to i[1].g.a[*0*] @@ -877,8 +921,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4), 100); - ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager)); - replacement7.Apply(context.get(), &fact_manager); + ASSERT_TRUE(replacement7.IsApplicable(context.get(), transformation_context)); + replacement7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replacements of the form %21 -> %101 @@ -890,7 +934,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1), 101); - ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement8.IsApplicable(context.get(), transformation_context)); // %41 = OpAccessChain %19 %37 *%21* %16 %21 // Corresponds to h.*g*.a[1] @@ -899,7 +944,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1), 101); - ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement9.IsApplicable(context.get(), transformation_context)); // %41 = OpAccessChain %19 %37 %21 %16 *%21* // Corresponds to h.g.a[*1*] @@ -908,8 +954,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3), 101); - ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager)); - replacement10.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement10.IsApplicable(context.get(), transformation_context)); + replacement10.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // %44 = OpAccessChain %23 %37 *%21* %21 %43 @@ -919,7 +966,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1), 101); - ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement11.IsApplicable(context.get(), transformation_context)); // %44 = OpAccessChain %23 %37 %21 *%21* %43 // Corresponds to h.g.*b*[0] @@ -928,7 +976,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2), 101); - ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement12.IsApplicable(context.get(), transformation_context)); // %46 = OpAccessChain %26 %37 *%21* %17 // Corresponds to h.*g*.c @@ -937,7 +986,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1), 101); - ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement13.IsApplicable(context.get(), transformation_context)); // %53 = OpAccessChain %19 %50 *%21* %21 %16 %16 // Corresponds to i[*1*].g.a[0] @@ -946,8 +996,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1), 101); - ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager)); - replacement14.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement14.IsApplicable(context.get(), transformation_context)); + replacement14.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // %53 = OpAccessChain %19 %50 %21 *%21* %16 %16 @@ -957,7 +1008,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2), 101); - ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement15.IsApplicable(context.get(), transformation_context)); // %56 = OpAccessChain %23 %50 %17 *%21* %21 %55 // Corresponds to i[2].*g*.b[1] @@ -966,7 +1018,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2), 101); - ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement16.IsApplicable(context.get(), transformation_context)); // %56 = OpAccessChain %23 %50 %17 %21 *%21* %55 // Corresponds to i[2].g.*b*[1] @@ -975,7 +1028,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3), 101); - ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement17.IsApplicable(context.get(), transformation_context)); // %58 = OpAccessChain %26 %50 %57 *%21* %17 // Corresponds to i[3].*g*.c @@ -984,7 +1038,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 21, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 2), 101); - ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement18.IsApplicable(context.get(), transformation_context)); // Replacements of the form %17 -> %102 @@ -995,8 +1050,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2), 102); - ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager)); - replacement19.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement19.IsApplicable(context.get(), transformation_context)); + replacement19.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // %27 = OpAccessChain %26 %15 %17 @@ -1006,7 +1062,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1), 102); - ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement20.IsApplicable(context.get(), transformation_context)); // %46 = OpAccessChain %26 %37 %21 %17 // Corresponds to h.g.*c* @@ -1015,7 +1072,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2), 102); - ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement21.IsApplicable(context.get(), transformation_context)); // %56 = OpAccessChain %23 %50 %17 %21 %21 %55 // Corresponds to i[*2*].g.b[1] @@ -1024,8 +1082,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1), 102); - ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager)); - replacement22.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement22.IsApplicable(context.get(), transformation_context)); + replacement22.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // %58 = OpAccessChain %26 %50 %57 %21 %17 @@ -1035,7 +1094,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 17, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 3), 102); - ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + replacement23.IsApplicable(context.get(), transformation_context)); // Replacements of the form %57 -> %103 @@ -1046,8 +1106,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 57, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 1), 103); - ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager)); - replacement24.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement24.IsApplicable(context.get(), transformation_context)); + replacement24.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replacements of the form %32 -> %106 @@ -1059,8 +1120,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1), 106); - ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager)); - replacement25.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement25.IsApplicable(context.get(), transformation_context)); + replacement25.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replacements of the form %43 -> %107 @@ -1072,8 +1134,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3), 107); - ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager)); - replacement26.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement26.IsApplicable(context.get(), transformation_context)); + replacement26.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replacements of the form %55 -> %108 @@ -1085,8 +1148,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor( 55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4), 108); - ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager)); - replacement27.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement27.IsApplicable(context.get(), transformation_context)); + replacement27.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Replacements of the form %8 -> %109 @@ -1098,8 +1162,9 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 2), 109); - ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager)); - replacement28.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + replacement28.IsApplicable(context.get(), transformation_context)); + replacement28.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); const std::string after_transformation = R"( diff --git a/test/fuzz/transformation_set_function_control_test.cpp b/test/fuzz/transformation_set_function_control_test.cpp index 536e965e39..be7f2be4c0 100644 --- a/test/fuzz/transformation_set_function_control_test.cpp +++ b/test/fuzz/transformation_set_function_control_test.cpp @@ -118,41 +118,48 @@ TEST(TransformationSetFunctionControlTest, VariousScenarios) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // %36 is not a function ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // 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)); + .IsApplicable(context.get(), transformation_context)); // 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)); + .IsApplicable(context.get(), transformation_context)); // Set to None, removing Const TransformationSetFunctionControl transformation1(11, SpvFunctionControlMaskNone); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); // 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); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); // Set to Pure, removing DontInline TransformationSetFunctionControl transformation3(17, SpvFunctionControlPureMask); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); // Change from Inline to DontInline TransformationSetFunctionControl transformation4( 13, SpvFunctionControlDontInlineMask); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_set_loop_control_test.cpp b/test/fuzz/transformation_set_loop_control_test.cpp index 83953ec0e6..531aa7ade1 100644 --- a/test/fuzz/transformation_set_loop_control_test.cpp +++ b/test/fuzz/transformation_set_loop_control_test.cpp @@ -256,6 +256,9 @@ TEST(TransformationSetLoopControlTest, VariousScenarios) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // These are the loop headers together with the selection controls of their // merge instructions: @@ -275,310 +278,310 @@ TEST(TransformationSetLoopControlTest, VariousScenarios) { // 2 5 90 4 7 14 ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl( 10, SpvLoopControlDependencyInfiniteMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl( 10, SpvLoopControlIterationMultipleMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl( 10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 3, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 3, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 3, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl( 23, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 3, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask | SpvLoopControlPartialCountMask, 0, 10) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl( 43, SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(43, SpvLoopControlDependencyInfiniteMask | SpvLoopControlDependencyLengthMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl( 53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(53, SpvLoopControlDependencyInfiniteMask | SpvLoopControlDependencyLengthMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 53, SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 5, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 5, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlPeelCountMask, 23, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl( 63, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlPeelCountMask, 2, 23) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl( 73, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 5, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask | SpvLoopControlPeelCountMask, 23, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl( 73, SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask | SpvLoopControlPeelCountMask, 2, 23) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl( 83, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 5, 3) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(83, SpvLoopControlUnrollMask | SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 23, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(83, SpvLoopControlUnrollMask | SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 2, 23) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl( 93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask | SpvLoopControlPartialCountMask, 0, 60) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl( 113, SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( TransformationSetLoopControl( 123, @@ -586,72 +589,72 @@ TEST(TransformationSetLoopControlTest, VariousScenarios) { SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 7, 8) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, 0, 9) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSetLoopControl( 123, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, 7, 9) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSetLoopControl( 123, SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, 7, 9) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationSetLoopControl(10, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 3, 3) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl( 43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask, 0, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(63, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlPeelCountMask, 23, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(73, SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask | SpvLoopControlPeelCountMask, 23, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl( 93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); TransformationSetLoopControl( 123, SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, 0, 9) - .Apply(context.get(), &fact_manager); + .Apply(context.get(), &transformation_context); std::string after_transformation = R"( OpCapability Shader @@ -942,25 +945,28 @@ TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) { BuildModule(SPV_ENV_UNIVERSAL_1_5, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); 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_FALSE(set_peel_and_partial.IsApplicable(context_1_0.get(), + transformation_context)); + ASSERT_FALSE(set_peel_and_partial.IsApplicable(context_1_1.get(), + transformation_context)); + ASSERT_FALSE(set_peel_and_partial.IsApplicable(context_1_2.get(), + transformation_context)); + ASSERT_FALSE(set_peel_and_partial.IsApplicable(context_1_3.get(), + transformation_context)); - 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)); + ASSERT_TRUE(set_peel_and_partial.IsApplicable(context_1_4.get(), + transformation_context)); + ASSERT_TRUE(set_peel_and_partial.IsApplicable(context_1_5.get(), + transformation_context)); } } // namespace diff --git a/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp index ad4dc2567e..c02d8d400b 100644 --- a/test/fuzz/transformation_set_memory_operands_mask_test.cpp +++ b/test/fuzz/transformation_set_memory_operands_mask_test.cpp @@ -92,37 +92,41 @@ TEST(TransformationSetMemoryOperandsMaskTest, PreSpirv14) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Not OK: the instruction is not a memory access. ASSERT_FALSE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(21, SpvOpAccessChain, 0), SpvMemoryAccessMaskNone, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to remove Aligned ASSERT_FALSE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(147, SpvOpLoad, 0), SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationSetMemoryOperandsMask transformation1( MakeInstructionDescriptor(147, SpvOpLoad, 0), SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); // Not OK to remove Aligned ASSERT_FALSE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), SpvMemoryAccessMaskNone, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK: leaves the mask as is ASSERT_TRUE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), SpvMemoryAccessAlignedMask, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK: adds Nontemporal and Volatile TransformationSetMemoryOperandsMask transformation2( @@ -130,41 +134,45 @@ TEST(TransformationSetMemoryOperandsMaskTest, PreSpirv14) { SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); // Not OK to remove Volatile ASSERT_FALSE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), SpvMemoryAccessNontemporalMask, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Not OK to add Aligned ASSERT_FALSE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK: adds Nontemporal TransformationSetMemoryOperandsMask transformation3( MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); // OK: adds Nontemporal and Volatile TransformationSetMemoryOperandsMask transformation4( MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); // OK: removes Nontemporal, adds Volatile TransformationSetMemoryOperandsMask transformation5( MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); std::string after_transformation = R"( OpCapability Shader @@ -306,6 +314,9 @@ TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); TransformationSetMemoryOperandsMask transformation1( MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), @@ -314,9 +325,10 @@ TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) { ASSERT_FALSE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), SpvMemoryAccessVolatileMask, 1) - .IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); TransformationSetMemoryOperandsMask transformation2( MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), @@ -325,9 +337,10 @@ TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) { ASSERT_FALSE(TransformationSetMemoryOperandsMask( MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), SpvMemoryAccessNontemporalMask, 0) - .IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); TransformationSetMemoryOperandsMask transformation3( MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), @@ -337,27 +350,31 @@ TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) { MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 0) - .IsApplicable(context.get(), fact_manager)); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); TransformationSetMemoryOperandsMask transformation4( MakeInstructionDescriptor(138, SpvOpCopyMemory, 1), SpvMemoryAccessVolatileMask, 1); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); TransformationSetMemoryOperandsMask transformation5( MakeInstructionDescriptor(147, SpvOpLoad, 0), SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); TransformationSetMemoryOperandsMask transformation6( MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone, 0); - ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); - transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + transformation6.Apply(context.get(), &transformation_context); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_set_selection_control_test.cpp b/test/fuzz/transformation_set_selection_control_test.cpp index 9696417b0d..9afb89d6df 100644 --- a/test/fuzz/transformation_set_selection_control_test.cpp +++ b/test/fuzz/transformation_set_selection_control_test.cpp @@ -103,39 +103,46 @@ TEST(TransformationSetSelectionControlTest, VariousScenarios) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // %44 is not a block ASSERT_FALSE( TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %13 does not end with OpSelectionMerge ASSERT_FALSE( TransformationSetSelectionControl(13, SpvSelectionControlMaskNone) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // %10 ends in OpLoopMerge, not OpSelectionMerge ASSERT_FALSE( TransformationSetSelectionControl(10, SpvSelectionControlMaskNone) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); TransformationSetSelectionControl transformation1( 11, SpvSelectionControlDontFlattenMask); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); TransformationSetSelectionControl transformation2( 23, SpvSelectionControlFlattenMask); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); TransformationSetSelectionControl transformation3( 31, SpvSelectionControlMaskNone); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); TransformationSetSelectionControl transformation4( 31, SpvSelectionControlFlattenMask); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_split_block_test.cpp b/test/fuzz/transformation_split_block_test.cpp index 09007a5b6d..8e9e8a4f13 100644 --- a/test/fuzz/transformation_split_block_test.cpp +++ b/test/fuzz/transformation_split_block_test.cpp @@ -89,57 +89,60 @@ TEST(TransformationSplitBlockTest, NotApplicable) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // No split before OpVariable ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(8, SpvOpVariable, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(8, SpvOpVariable, 1), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No split before OpLabel ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(14, SpvOpLabel, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No split if base instruction is outside a function ASSERT_FALSE( TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No split if block is loop header ASSERT_FALSE( TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No split if base instruction does not exist ASSERT_FALSE( TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(88, SpvOpIMul, 22), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No split if too many instructions with the desired opcode are skipped ASSERT_FALSE( TransformationSplitBlock( MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // No split if id in use ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) { @@ -199,11 +202,14 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto split_1 = TransformationSplitBlock( MakeInstructionDescriptor(5, SpvOpStore, 0), 100); - ASSERT_TRUE(split_1.IsApplicable(context.get(), fact_manager)); - split_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(split_1.IsApplicable(context.get(), transformation_context)); + split_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_split_1 = R"( @@ -250,8 +256,8 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) { auto split_2 = TransformationSplitBlock( MakeInstructionDescriptor(11, SpvOpStore, 0), 101); - ASSERT_TRUE(split_2.IsApplicable(context.get(), fact_manager)); - split_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(split_2.IsApplicable(context.get(), transformation_context)); + split_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_split_2 = R"( @@ -300,8 +306,8 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) { auto split_3 = TransformationSplitBlock( MakeInstructionDescriptor(14, SpvOpLoad, 0), 102); - ASSERT_TRUE(split_3.IsApplicable(context.get(), fact_manager)); - split_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(split_3.IsApplicable(context.get(), transformation_context)); + split_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_split_3 = R"( @@ -412,21 +418,24 @@ TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Illegal to split between the merge and the conditional branch. ASSERT_FALSE( TransformationSplitBlock( MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSplitBlock( MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto split = TransformationSplitBlock( MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100); - ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager)); - split.Apply(context.get(), &fact_manager); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + split.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_split = R"( @@ -541,19 +550,22 @@ TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Illegal to split between the merge and the conditional branch. ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationSplitBlock( MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); auto split = TransformationSplitBlock( MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100); - ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager)); - split.Apply(context.get(), &fact_manager); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + split.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_split = R"( @@ -674,18 +686,21 @@ TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // We cannot split before OpPhi instructions, since the number of incoming // blocks may not appropriately match after splitting. ASSERT_FALSE( TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) { @@ -726,16 +741,19 @@ TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); ASSERT_TRUE( TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // An equivalent transformation to the above, just described with respect to a // different base instruction. auto split = TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100); - ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager)); - split.Apply(context.get(), &fact_manager); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + split.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_split = R"( @@ -805,18 +823,21 @@ TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Record the fact that block 8 is dead. - fact_manager.AddFactBlockIsDead(8); + transformation_context.GetFactManager()->AddFactBlockIsDead(8); auto split = TransformationSplitBlock( MakeInstructionDescriptor(8, SpvOpBranch, 0), 100); - ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager)); - split.Apply(context.get(), &fact_manager); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + split.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); - ASSERT_TRUE(fact_manager.BlockIsDead(8)); - ASSERT_TRUE(fact_manager.BlockIsDead(100)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(8)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100)); std::string after_split = R"( OpCapability Shader diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp index 3fb9b6154d..067a5a1199 100644 --- a/test/fuzz/transformation_store_test.cpp +++ b/test/fuzz/transformation_store_test.cpp @@ -94,16 +94,26 @@ TEST(TransformationStoreTest, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - - fact_manager.AddFactValueOfPointeeIsIrrelevant(27); - fact_manager.AddFactValueOfPointeeIsIrrelevant(11); - fact_manager.AddFactValueOfPointeeIsIrrelevant(46); - fact_manager.AddFactValueOfPointeeIsIrrelevant(16); - fact_manager.AddFactValueOfPointeeIsIrrelevant(52); - fact_manager.AddFactValueOfPointeeIsIrrelevant(81); - fact_manager.AddFactValueOfPointeeIsIrrelevant(82); - - fact_manager.AddFactBlockIsDead(36); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 27); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 11); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 46); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 16); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 52); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 81); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 82); + + transformation_context.GetFactManager()->AddFactBlockIsDead(36); // Variables with pointee types: // 52 - ptr_to(7) @@ -139,90 +149,91 @@ TEST(TransformationStoreTest, BasicTest) { // Bad: attempt to store to 11 from outside its function ASSERT_FALSE(TransformationStore( 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available ASSERT_FALSE(TransformationStore( 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable ASSERT_FALSE(TransformationStore( 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist ASSERT_FALSE(TransformationStore( 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists but does not have a type ASSERT_FALSE(TransformationStore( 5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists and has a type, but is not a pointer ASSERT_FALSE(TransformationStore( 24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to a null pointer ASSERT_FALSE(TransformationStore( 60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to an undefined pointer ASSERT_FALSE(TransformationStore( 61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: %82 is not available at the program point ASSERT_FALSE( TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: value id does not exist ASSERT_FALSE(TransformationStore( 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: value id exists but does not have a type ASSERT_FALSE(TransformationStore( 27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: value id exists but has the wrong type ASSERT_FALSE(TransformationStore( 27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to read-only variable ASSERT_FALSE(TransformationStore( 92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: value is not available ASSERT_FALSE(TransformationStore( 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Bad: variable being stored to does not have an irrelevant pointee value, // and the store is not in a dead block. ASSERT_FALSE(TransformationStore( 20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // The described instruction does not exist. ASSERT_FALSE(TransformationStore( 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); { // Store to irrelevant variable from dead block. TransformationStore transformation( 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -230,8 +241,9 @@ TEST(TransformationStoreTest, BasicTest) { // Store to irrelevant variable from live block. TransformationStore transformation( 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -239,8 +251,9 @@ TEST(TransformationStoreTest, BasicTest) { // Store to irrelevant variable from live block. TransformationStore transformation( 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -248,8 +261,9 @@ TEST(TransformationStoreTest, BasicTest) { // Store to irrelevant variable from live block. TransformationStore transformation( 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } @@ -257,8 +271,9 @@ TEST(TransformationStoreTest, BasicTest) { // Store to non-irrelevant variable from dead block. TransformationStore transformation( 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); - ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); - transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } diff --git a/test/fuzz/transformation_swap_commutable_operands_test.cpp b/test/fuzz/transformation_swap_commutable_operands_test.cpp index f0591cf1c2..c213dfeab8 100644 --- a/test/fuzz/transformation_swap_commutable_operands_test.cpp +++ b/test/fuzz/transformation_swap_commutable_operands_test.cpp @@ -111,113 +111,140 @@ TEST(TransformationSwapCommutableOperandsTest, IsApplicableTest) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); - FactManager factManager; + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Tests existing commutative instructions auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0); auto transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests existing non-commutative instructions instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(8, SpvOpConstant, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(11, SpvOpVariable, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(14, SpvOpConstantComposite, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests the base instruction id not existing instructionDescriptor = MakeInstructionDescriptor(67, SpvOpIAddCarry, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(68, SpvOpIEqual, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(69, SpvOpINotEqual, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(70, SpvOpFOrdEqual, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(71, SpvOpPtrEqual, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests there being no instruction with the desired opcode after the base // instruction id instructionDescriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(38, SpvOpIMul, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(45, SpvOpFAdd, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(66, SpvOpFMul, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests there being an instruction with the desired opcode after the base // instruction id, but the skip count associated with the instruction // descriptor being so high. instructionDescriptor = MakeInstructionDescriptor(11, SpvOpIAdd, 100); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(16, SpvOpIMul, 100); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(23, SpvOpFAdd, 100); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(32, SpvOpFMul, 100); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(37, SpvOpDot, 100); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationSwapCommutableOperandsTest, ApplyTest) { @@ -311,28 +338,31 @@ TEST(TransformationSwapCommutableOperandsTest, ApplyTest) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); - FactManager factManager; + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0); auto transformation = TransformationSwapCommutableOperands(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0); transformation = TransformationSwapCommutableOperands(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); std::string variantShader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp index 98e0a6442e..b20f59e086 100644 --- a/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp +++ b/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp @@ -111,78 +111,93 @@ TEST(TransformationToggleAccessChainInstructionTest, IsApplicableTest) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); - FactManager factManager; + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Tests existing access chain instructions auto instructionDescriptor = MakeInstructionDescriptor(18, SpvOpAccessChain, 0); auto transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests existing non-access chain instructions instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(14, SpvOpConstantComposite, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests the base instruction id not existing instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests there being no instruction with the desired opcode after the base // instruction id instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); // Tests there being an instruction with the desired opcode after the base // instruction id, but the skip count associated with the instruction @@ -190,13 +205,15 @@ TEST(TransformationToggleAccessChainInstructionTest, IsApplicableTest) { instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); instructionDescriptor = MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) { @@ -290,35 +307,38 @@ TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) { const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); - FactManager factManager; + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); auto instructionDescriptor = MakeInstructionDescriptor(18, SpvOpAccessChain, 0); auto transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0); transformation = TransformationToggleAccessChainInstruction(instructionDescriptor); - transformation.Apply(context.get(), &factManager); + transformation.Apply(context.get(), &transformation_context); std::string variantShader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_vector_shuffle_test.cpp b/test/fuzz/transformation_vector_shuffle_test.cpp index 385c38b444..e3c7fd4483 100644 --- a/test/fuzz/transformation_vector_shuffle_test.cpp +++ b/test/fuzz/transformation_vector_shuffle_test.cpp @@ -86,249 +86,259 @@ TEST(TransformationVectorShuffle, BasicTest) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}), - MakeDataDescriptor(12, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}), - MakeDataDescriptor(12, {1}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}), - MakeDataDescriptor(16, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}), - MakeDataDescriptor(16, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}), - MakeDataDescriptor(16, {2}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}), - MakeDataDescriptor(20, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}), - MakeDataDescriptor(20, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}), - MakeDataDescriptor(20, {2}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}), - MakeDataDescriptor(20, {3}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}), - MakeDataDescriptor(27, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}), - MakeDataDescriptor(27, {1}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}), - MakeDataDescriptor(31, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}), - MakeDataDescriptor(31, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}), - MakeDataDescriptor(31, {2}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}), - MakeDataDescriptor(35, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}), - MakeDataDescriptor(35, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}), - MakeDataDescriptor(35, {2}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}), - MakeDataDescriptor(35, {3}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}), - MakeDataDescriptor(42, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}), - MakeDataDescriptor(42, {1}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}), - MakeDataDescriptor(46, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}), - MakeDataDescriptor(46, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}), - MakeDataDescriptor(46, {2}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}), - MakeDataDescriptor(50, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}), - MakeDataDescriptor(50, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}), - MakeDataDescriptor(50, {2}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}), - MakeDataDescriptor(50, {3}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}), - MakeDataDescriptor(61, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}), - MakeDataDescriptor(61, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}), - MakeDataDescriptor(61, {2}), context.get()); - - fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}), - MakeDataDescriptor(65, {0}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}), - MakeDataDescriptor(65, {1}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}), - MakeDataDescriptor(65, {2}), context.get()); - fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}), - MakeDataDescriptor(65, {3}), context.get()); + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(12, {1}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(16, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(16, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(16, {2}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(20, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(20, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(20, {2}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(20, {3}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(27, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(27, {1}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(31, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(31, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(31, {2}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(35, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(35, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(35, {2}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(35, {3}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(42, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(42, {1}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(46, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(46, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(46, {2}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(50, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(50, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(50, {2}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(50, {3}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(61, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(56, {}), MakeDataDescriptor(61, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(61, {2}), context.get()); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(65, {0}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(56, {}), MakeDataDescriptor(65, {1}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(65, {2}), context.get()); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(56, {}), MakeDataDescriptor(65, {3}), context.get()); // %103 does not dominate the return instruction. ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 103, 65, {3, 5, 7}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Illegal to shuffle a bvec2 and a vec3 ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 112, 61, {0, 2, 4}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Illegal to shuffle an ivec2 and a uvec4 ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 50, {1, 3, 5}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Vector 1 does not exist ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 300, 50, {1, 3, 5}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Vector 2 does not exist ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 300, {1, 3, 5}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Index out of range ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 20}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Too many indices ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 1, 0, 1, 0, 1, 0, 1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Too few indices ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Too few indices again ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Indices define unknown type: we do not have vec2 ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 65, 65, {0, 1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // The instruction to insert before does not exist ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpCompositeConstruct, 1), 201, 20, 12, {0xFFFFFFFF, 3, 5}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // The 'fresh' id is already in use ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(100, SpvOpReturn, 0), 12, 12, 112, {}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); protobufs::DataDescriptor temp_dd; TransformationVectorShuffle transformation1( MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {1, 0}); - ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); - transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); temp_dd = MakeDataDescriptor(200, {0}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(200, {1}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(10, {}), temp_dd, context.get())); TransformationVectorShuffle transformation2( MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12, {0xFFFFFFFF, 3, 5}); - ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); - transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); temp_dd = MakeDataDescriptor(201, {1}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(201, {2}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), temp_dd, context.get())); TransformationVectorShuffle transformation3( MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1}); - ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); - transformation3.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + transformation3.Apply(context.get(), &transformation_context); temp_dd = MakeDataDescriptor(202, {0}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(26, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(202, {1}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(25, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(202, {2}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(26, {}), temp_dd, context.get())); TransformationVectorShuffle transformation4( MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1}); - ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); - transformation4.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + transformation4.Apply(context.get(), &transformation_context); temp_dd = MakeDataDescriptor(203, {0}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(203, {1}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd, context.get())); TransformationVectorShuffle transformation5( MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4}); - ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager)); - transformation5.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + transformation5.Apply(context.get(), &transformation_context); temp_dd = MakeDataDescriptor(204, {0}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(204, {1}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(204, {2}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd, context.get())); TransformationVectorShuffle transformation6( MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42, {0, 1, 2, 3}); - ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager)); - transformation6.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + transformation6.Apply(context.get(), &transformation_context); temp_dd = MakeDataDescriptor(205, {0}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(205, {1}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(205, {2}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd, context.get())); temp_dd = MakeDataDescriptor(205, {3}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd, context.get())); // swizzle vec4 from vec4 and vec4 using some undefs TransformationVectorShuffle transformation7( MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65, {0xFFFFFFFF, 3, 6, 0xFFFFFFFF}); - ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager)); - transformation7.Apply(context.get(), &fact_manager); + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + transformation7.Apply(context.get(), &transformation_context); temp_dd = MakeDataDescriptor(206, {1}); - ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(56, {}), temp_dd, - context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(56, {}), temp_dd, context.get())); std::string after_transformation = R"( OpCapability Shader @@ -479,52 +489,55 @@ TEST(TransformationVectorShuffleTest, IllegalInsertionPoints) { ASSERT_TRUE(IsValid(env, context.get())); FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); // Cannot insert before the OpVariables of a function. ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK to insert right after the OpVariables. ASSERT_FALSE( TransformationVectorShuffle( MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Cannot insert before the OpPhis of a block. ASSERT_FALSE( TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, 14, {2, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, 14, {3, 0}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // OK to insert after the OpPhis. ASSERT_TRUE(TransformationVectorShuffle( MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, 14, {3, 4}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Cannot insert before OpLoopMerge ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(33, SpvOpBranchConditional, 0), 200, 14, 14, {3}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); // Cannot insert before OpSelectionMerge ASSERT_FALSE(TransformationVectorShuffle( MakeInstructionDescriptor(21, SpvOpBranchConditional, 0), 200, 14, 14, {2}) - .IsApplicable(context.get(), fact_manager)); + .IsApplicable(context.get(), transformation_context)); } } // namespace diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index 469c81b48a..2c9807d1d2 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp @@ -580,7 +580,8 @@ int main(int argc, const char** argv) { switch (status.action) { case FuzzActions::FORCE_RENDER_RED: - if (!spvtools::fuzz::ForceRenderRed(target_env, binary_in, initial_facts, + if (!spvtools::fuzz::ForceRenderRed(target_env, validator_options, + binary_in, initial_facts, &binary_out)) { return 1; } From f28cdeff16f6c9076eeb780b2e709d6a516083c0 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Apr 2020 17:35:00 +0100 Subject: [PATCH 65/88] spirv-fuzz: Only replace regular ids with synonyms (#3255) Fixes an issue where an id use in a non-regular context, e.g. as a scope parameter to an atomic instruction, would be replaced with a synonym. --- source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 30884a6029..20258218bf 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -38,16 +38,20 @@ void FuzzerPassApplyIdSynonyms::Apply() { GetTransformationContext() ->GetFactManager() ->GetIdsForWhichSynonymsAreKnown(GetIRContext())) { - // Gather up all uses of |id_with_known_synonym|, and then subsequently - // iterate over these uses. We use this separation because, when - // considering a given use, we might apply a transformation that will + // Gather up all uses of |id_with_known_synonym| as a regular id, and + // subsequently iterate over these uses. We use this separation because, + // when considering a given use, we might apply a transformation that will // invalidate the def-use manager. std::vector> uses; GetIRContext()->get_def_use_mgr()->ForEachUse( id_with_known_synonyms, [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void { - uses.emplace_back( - std::pair(use_inst, use_index)); + // We only gather up regular id uses; e.g. we do not include a use of + // the id as the scope for an atomic operation. + if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) { + uses.emplace_back( + std::pair(use_inst, use_index)); + } }); for (auto& use : uses) { From bfd25ace0844b9bdf7134bb637c28d25f00f0278 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Apr 2020 17:35:18 +0100 Subject: [PATCH 66/88] spirv-fuzz: Limit adding of new variables to 'basic' types (#3257) To avoid problems where global and local variables of opaque or runtime-sized types are added to a module, this change introduces the notion of a 'basic type' -- a type made up from floats, ints, bools, or vectors, matrices, structs and fixed-size arrays of basic types. Added variables have to be of basic type. --- source/fuzz/fuzzer_pass.cpp | 71 +++++++++++++------ source/fuzz/fuzzer_pass.h | 21 +++--- .../fuzz/fuzzer_pass_add_global_variables.cpp | 42 +++++------ .../fuzz/fuzzer_pass_add_local_variables.cpp | 42 +++++------ 4 files changed, 105 insertions(+), 71 deletions(-) diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 4ab29466d5..dbe514338b 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -14,6 +14,8 @@ #include "source/fuzz/fuzzer_pass.h" +#include + #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/transformation_add_constant_boolean.h" @@ -329,43 +331,72 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { } std::pair, std::map>> -FuzzerPass::GetAvailableBaseTypesAndPointers( +FuzzerPass::GetAvailableBasicTypesAndPointers( SpvStorageClass storage_class) const { - // Records all of the base types available in the module. - std::vector base_types; + // Records all of the basic types available in the module. + std::set basic_types; - // For each base type, records all the associated pointer types that target - // that base type and that have |storage_class| as their storage class. - std::map> base_type_to_pointers; + // For each basic type, records all the associated pointer types that target + // the basic type and that have |storage_class| as their storage class. + std::map> basic_type_to_pointers; for (auto& inst : GetIRContext()->types_values()) { + // For each basic type that we come across, record type, and the fact that + // we cannot yet have seen any pointers that use the basic type as its + // pointee type. + // + // For pointer types with basic pointee types, associate the pointer type + // with the basic type. switch (inst.opcode()) { - case SpvOpTypeArray: case SpvOpTypeBool: case SpvOpTypeFloat: case SpvOpTypeInt: case SpvOpTypeMatrix: - case SpvOpTypeStruct: case SpvOpTypeVector: - // These types are suitable as pointer base types. Record the type, - // and the fact that we cannot yet have seen any pointers that use this - // as its base type. - base_types.push_back(inst.result_id()); - base_type_to_pointers.insert({inst.result_id(), {}}); + // These are all basic types. + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + break; + case SpvOpTypeArray: + // An array type is basic if its base type is basic. + if (basic_types.count(inst.GetSingleWordInOperand(0))) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + } break; - case SpvOpTypePointer: - if (inst.GetSingleWordInOperand(0) == storage_class) { - // The pointer has the desired storage class, so we are interested in - // it. Associate it with its base type. - base_type_to_pointers.at(inst.GetSingleWordInOperand(1)) - .push_back(inst.result_id()); + case SpvOpTypeStruct: { + // A struct type is basic if all of its members are basic. + bool all_members_are_basic_types = true; + for (uint32_t i = 0; i < inst.NumInOperands(); i++) { + if (!basic_types.count(inst.GetSingleWordInOperand(i))) { + all_members_are_basic_types = false; + break; + } + } + if (all_members_are_basic_types) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); } break; + } + case SpvOpTypePointer: { + // We are interested in the pointer if its pointee type is basic and it + // has the right storage class. + auto pointee_type = inst.GetSingleWordInOperand(1); + if (inst.GetSingleWordInOperand(0) == storage_class && + basic_types.count(pointee_type)) { + // The pointer has the desired storage class, and its pointee type is + // a basic type, so we are interested in it. Associate it with its + // basic type. + basic_type_to_pointers.at(pointee_type).push_back(inst.result_id()); + } + break; + } default: break; } } - return {base_types, base_type_to_pointers}; + return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers}; } uint32_t FuzzerPass::FindOrCreateZeroConstant( diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 436fd77852..94b8dfa444 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -169,18 +169,21 @@ class FuzzerPass { // If no such instruction exists, a transformation is applied to add it. uint32_t FindOrCreateGlobalUndef(uint32_t type_id); - // Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that: - // - base_type_ids captures every scalar or composite type declared in the - // module (i.e., all int, bool, float, vector, matrix, struct and array - // types - // - base_type_ids_to_pointers maps every such base type to the sequence + // Define a *basic type* to be an integer, boolean or floating-point type, + // or a matrix, vector, struct or fixed-size array built from basic types. In + // particular, a basic type cannot contain an opaque type (such as an image), + // or a runtime-sized array. + // + // Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that: + // - basic_type_ids captures every basic type declared in the module. + // - basic_type_ids_to_pointers maps every such basic type to the sequence // of all pointer types that have storage class |storage_class| and the - // given base type as their pointee type. The sequence may be empty for - // some base types if no pointers to those types are defined for the given + // given basic type as their pointee type. The sequence may be empty for + // some basic types if no pointers to those types are defined for the given // storage class, and the sequence will have multiple elements if there are - // repeated pointer declarations for the same base type and storage class. + // repeated pointer declarations for the same basic type and storage class. std::pair, std::map>> - GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const; + GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const; // Given a type id, |scalar_or_composite_type_id|, which must correspond to // some scalar or composite type, returns the result id of an instruction diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index ce2b8eb1bb..80708edfb8 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -30,45 +30,45 @@ FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default; void FuzzerPassAddGlobalVariables::Apply() { - auto base_type_ids_and_pointers = - GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate); + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate); - // These are the base types that are available to this fuzzer pass. - auto& base_types = base_type_ids_and_pointers.first; + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; - // These are the pointers to those base types that are *initially* available + // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where - // none are available for a given base type. - auto& base_type_to_pointers = base_type_ids_and_pointers.second; + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; // Probabilistically keep adding global variables. while (GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) { - // Choose a random base type; the new variable's type will be a pointer to - // this base type. - uint32_t base_type = - base_types[GetFuzzerContext()->RandomIndex(base_types)]; + // Choose a random basic type; the new variable's type will be a pointer to + // this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; uint32_t pointer_type_id; - std::vector& available_pointers_to_base_type = - base_type_to_pointers.at(base_type); - // Determine whether there is at least one pointer to this base type. - if (available_pointers_to_base_type.empty()) { + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { // There is not. Make one, to use here, and add it to the available - // pointers for the base type so that future variables can potentially + // pointers for the basic type so that future variables can potentially // use it. pointer_type_id = GetFuzzerContext()->GetFreshId(); - available_pointers_to_base_type.push_back(pointer_type_id); + available_pointers_to_basic_type.push_back(pointer_type_id); ApplyTransformation(TransformationAddTypePointer( - pointer_type_id, SpvStorageClassPrivate, base_type)); + pointer_type_id, SpvStorageClassPrivate, basic_type)); } else { // There is - grab one. pointer_type_id = - available_pointers_to_base_type[GetFuzzerContext()->RandomIndex( - available_pointers_to_base_type)]; + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; } ApplyTransformation(TransformationAddGlobalVariable( GetFuzzerContext()->GetFreshId(), pointer_type_id, - FindOrCreateZeroConstant(base_type), true)); + FindOrCreateZeroConstant(basic_type), true)); } } diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp index ace9be266b..661159e659 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -31,47 +31,47 @@ FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default; void FuzzerPassAddLocalVariables::Apply() { - auto base_type_ids_and_pointers = - GetAvailableBaseTypesAndPointers(SpvStorageClassFunction); + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(SpvStorageClassFunction); - // These are the base types that are available to this fuzzer pass. - auto& base_types = base_type_ids_and_pointers.first; + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; - // These are the pointers to those base types that are *initially* available + // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where - // none are available for a given base type. - auto& base_type_to_pointers = base_type_ids_and_pointers.second; + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; // Consider every function in the module. for (auto& function : *GetIRContext()->module()) { // Probabilistically keep adding random variables to this function. while (GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingLocalVariable())) { - // Choose a random base type; the new variable's type will be a pointer to - // this base type. - uint32_t base_type = - base_types[GetFuzzerContext()->RandomIndex(base_types)]; + // Choose a random basic type; the new variable's type will be a pointer + // to this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; uint32_t pointer_type; - std::vector& available_pointers_to_base_type = - base_type_to_pointers.at(base_type); - // Determine whether there is at least one pointer to this base type. - if (available_pointers_to_base_type.empty()) { + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { // There is not. Make one, to use here, and add it to the available - // pointers for the base type so that future variables can potentially + // pointers for the basic type so that future variables can potentially // use it. pointer_type = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationAddTypePointer( - pointer_type, SpvStorageClassFunction, base_type)); - available_pointers_to_base_type.push_back(pointer_type); + pointer_type, SpvStorageClassFunction, basic_type)); + available_pointers_to_basic_type.push_back(pointer_type); } else { // There is - grab one. pointer_type = - available_pointers_to_base_type[GetFuzzerContext()->RandomIndex( - available_pointers_to_base_type)]; + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; } ApplyTransformation(TransformationAddLocalVariable( GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(), - FindOrCreateZeroConstant(base_type), true)); + FindOrCreateZeroConstant(basic_type), true)); } } } From 5d491a7ed66043459c8ce3144f9f5949f8d43f2e Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Apr 2020 18:33:19 +0100 Subject: [PATCH 67/88] spirv-fuzz: Handle isomorphic types property in composite construction (#3262) The fuzzer pass that constructs composites had an issue where it would regard isomorphic but distinct structs (similarly arrays) as being interchangeable when constructing composites. This change fixes the problem by relying less on the type manager. --- .../fuzz/fuzzer_pass_construct_composites.cpp | 192 +++++++++--------- .../fuzz/fuzzer_pass_construct_composites.h | 27 +-- test/fuzz/CMakeLists.txt | 1 + .../fuzzer_pass_construct_composites_test.cpp | 187 +++++++++++++++++ 4 files changed, 302 insertions(+), 105 deletions(-) create mode 100644 test/fuzz/fuzzer_pass_construct_composites_test.cpp diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index b1b08230bf..e78f8ece00 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -88,7 +88,7 @@ void FuzzerPassConstructComposites::Apply() { // 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> constructor_arguments = nullptr; + std::vector constructor_arguments; // Initially, all composite type ids are available for us to try. Keep // trying until we run out of options. @@ -96,35 +96,38 @@ void FuzzerPassConstructComposites::Apply() { 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); + GetFuzzerContext()->RemoveAtRandomIndex( + &composites_to_try_constructing); // 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( + auto composite_type_inst = GetIRContext()->get_def_use_mgr()->GetDef( 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); + switch (composite_type_inst->opcode()) { + case SpvOpTypeArray: + constructor_arguments = FindComponentsToConstructArray( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeMatrix: + constructor_arguments = FindComponentsToConstructMatrix( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeStruct: + constructor_arguments = FindComponentsToConstructStruct( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeVector: + constructor_arguments = FindComponentsToConstructVector( + *composite_type_inst, type_id_to_available_instructions); + break; + default: + assert(false && + "The space of possible composite types should be covered " + "by the above cases."); + break; } - if (constructor_arguments != nullptr) { + if (!constructor_arguments.empty()) { // We succeeded! Note the composite type we finally settled on, and // exit from the loop. chosen_composite_type = next_composite_to_try_constructing; @@ -135,21 +138,15 @@ void FuzzerPassConstructComposites::Apply() { 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); + assert(constructor_arguments.empty()); return; } - assert(constructor_arguments != nullptr); + assert(!constructor_arguments.empty()); // Make and apply a transformation. - TransformationCompositeConstruct transformation( - chosen_composite_type, *constructor_arguments, - instruction_descriptor, GetFuzzerContext()->GetFreshId()); - assert(transformation.IsApplicable(GetIRContext(), - *GetTransformationContext()) && - "This transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetTransformationContext()); - *GetTransformations()->add_transformation() = - transformation.ToMessage(); + ApplyTransformation(TransformationCompositeConstruct( + chosen_composite_type, constructor_arguments, + instruction_descriptor, GetFuzzerContext()->GetFreshId())); }); } @@ -162,20 +159,15 @@ void FuzzerPassConstructComposites::RecordAvailableInstruction( type_id_to_available_instructions->at(inst->type_id()).push_back(inst); } -std::unique_ptr> -FuzzerPassConstructComposites::TryConstructingArrayComposite( - const opt::analysis::Array& array_type, +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructArray( + const opt::Instruction& array_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions) { - // At present we assume arrays have a constant size. - assert(array_type.length_info().words.size() == 2); - assert(array_type.length_info().words[0] == - opt::analysis::Array::LengthInfo::kConstant); - - auto result = MakeUnique>(); + assert(array_type_instruction.opcode() == SpvOpTypeArray && + "Precondition: instruction must be an array type."); // Get the element type for the array. - auto element_type_id = - GetIRContext()->get_type_mgr()->GetId(array_type.element_type()); + auto element_type_id = array_type_instruction.GetSingleWordInOperand(0); // Get all instructions at our disposal that compute something of this element // type. @@ -186,26 +178,34 @@ FuzzerPassConstructComposites::TryConstructingArrayComposite( // If there are not any instructions available that compute the element type // of the array then we are not in a position to construct a composite with // this array type. - return nullptr; + return {}; } - 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()); + + uint32_t array_length = + GetIRContext() + ->get_def_use_mgr() + ->GetDef(array_type_instruction.GetSingleWordInOperand(1)) + ->GetSingleWordInOperand(0); + + std::vector result; + for (uint32_t index = 0; index < array_length; index++) { + result.push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); } return result; } -std::unique_ptr> -FuzzerPassConstructComposites::TryConstructingMatrixComposite( - const opt::analysis::Matrix& matrix_type, +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructMatrix( + const opt::Instruction& matrix_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions) { - auto result = MakeUnique>(); + assert(matrix_type_instruction.opcode() == SpvOpTypeMatrix && + "Precondition: instruction must be a matrix type."); // Get the element type for the matrix. - auto element_type_id = - GetIRContext()->get_type_mgr()->GetId(matrix_type.element_type()); + auto element_type_id = matrix_type_instruction.GetSingleWordInOperand(0); // Get all instructions at our disposal that compute something of this element // type. @@ -216,25 +216,32 @@ FuzzerPassConstructComposites::TryConstructingMatrixComposite( // If there are not any instructions available that compute the element type // of the matrix then we are not in a position to construct a composite with // this matrix type. - return nullptr; + return {}; } - for (uint32_t index = 0; index < matrix_type.element_count(); index++) { - result->push_back(available_instructions - ->second[GetFuzzerContext()->RandomIndex( - available_instructions->second)] - ->result_id()); + std::vector result; + for (uint32_t index = 0; + index < matrix_type_instruction.GetSingleWordInOperand(1); index++) { + result.push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); } return result; } -std::unique_ptr> -FuzzerPassConstructComposites::TryConstructingStructComposite( - const opt::analysis::Struct& struct_type, +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructStruct( + const opt::Instruction& struct_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions) { - auto result = MakeUnique>(); + assert(struct_type_instruction.opcode() == SpvOpTypeStruct && + "Precondition: instruction must be a struct type."); + std::vector result; // Consider the type of each field of the struct. - for (auto element_type : struct_type.element_types()) { - auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type); + for (uint32_t in_operand_index = 0; + in_operand_index < struct_type_instruction.NumInOperands(); + in_operand_index++) { + auto element_type_id = + struct_type_instruction.GetSingleWordInOperand(in_operand_index); // Find the instructions at our disposal that compute something of the field // type. auto available_instructions = @@ -242,24 +249,28 @@ FuzzerPassConstructComposites::TryConstructingStructComposite( if (available_instructions == type_id_to_available_instructions.cend()) { // If there are no such instructions, we cannot construct a composite of // this struct type. - return nullptr; + return {}; } - result->push_back(available_instructions - ->second[GetFuzzerContext()->RandomIndex( - available_instructions->second)] - ->result_id()); + result.push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); } return result; } -std::unique_ptr> -FuzzerPassConstructComposites::TryConstructingVectorComposite( - const opt::analysis::Vector& vector_type, +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructVector( + const opt::Instruction& vector_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions) { + assert(vector_type_instruction.opcode() == SpvOpTypeVector && + "Precondition: instruction must be a vector type."); + // 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(); + auto element_type_id = vector_type_instruction.GetSingleWordInOperand(0); + auto element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id); + auto element_count = vector_type_instruction.GetSingleWordInOperand(1); // 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 @@ -270,14 +281,12 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite( std::map 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_id_to_width[scalar_type_id] = 1; + smaller_vector_type_id_to_width[element_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); + opt::analysis::Vector smaller_vector_type(element_type, width); auto smaller_vector_type_id = GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type); // We might find that there is no declared type of this smaller width. @@ -304,12 +313,11 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite( // order at this stage. std::vector instructions_to_use; - while (vector_slots_used < vector_type.element_count()) { + while (vector_slots_used < element_count) { std::vector 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)) { + std::min(element_count - 1, element_count - vector_slots_used)) { continue; } auto available_instructions = @@ -328,7 +336,7 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite( // another manner, so we could opt to retry a few times here, but it is // simpler to just give up on the basis that this will not happen // frequently. - return nullptr; + return {}; } auto instruction_to_use = instructions_to_choose_from[GetFuzzerContext()->RandomIndex( @@ -347,16 +355,16 @@ FuzzerPassConstructComposites::TryConstructingVectorComposite( vector_slots_used += 1; } } - assert(vector_slots_used == vector_type.element_count()); + assert(vector_slots_used == element_count); - auto result = MakeUnique>(); + std::vector result; std::vector operands; while (!instructions_to_use.empty()) { auto index = GetFuzzerContext()->RandomIndex(instructions_to_use); - result->push_back(instructions_to_use[index]->result_id()); + result.push_back(instructions_to_use[index]->result_id()); instructions_to_use.erase(instructions_to_use.begin() + index); } - assert(result->size() > 1); + assert(result.size() > 1); return result; } diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h index d293514d3b..9853fadf03 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.h +++ b/source/fuzz/fuzzer_pass_construct_composites.h @@ -49,27 +49,28 @@ class FuzzerPassConstructComposites : public FuzzerPass { opt::Instruction* inst, TypeIdToInstructions* type_id_to_available_instructions); + // Requires that |array_type_instruction| has opcode OpTypeArray. // 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> TryConstructingArrayComposite( - const opt::analysis::Array& array_type, + // |array_type_instruction| to be constructed. Returns said ids if they can + // be found and an empty vector otherwise. + std::vector FindComponentsToConstructArray( + const opt::Instruction& array_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions); - // Similar to TryConstructingArrayComposite, but for matrices. - std::unique_ptr> TryConstructingMatrixComposite( - const opt::analysis::Matrix& matrix_type, + // Similar to FindComponentsToConstructArray, but for matrices. + std::vector FindComponentsToConstructMatrix( + const opt::Instruction& matrix_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions); - // Similar to TryConstructingArrayComposite, but for structs. - std::unique_ptr> TryConstructingStructComposite( - const opt::analysis::Struct& struct_type, + // Similar to FindComponentsToConstructArray, but for structs. + std::vector FindComponentsToConstructStruct( + const opt::Instruction& struct_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions); - // Similar to TryConstructingArrayComposite, but for vectors. - std::unique_ptr> TryConstructingVectorComposite( - const opt::analysis::Vector& vector_type, + // Similar to FindComponentsToConstructArray, but for vectors. + std::vector FindComponentsToConstructVector( + const opt::Instruction& vector_type_instruction, const TypeIdToInstructions& type_id_to_available_instructions); }; diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 99a78fd145..a8b09b9a8b 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -22,6 +22,7 @@ if (${SPIRV_BUILD_FUZZER}) fact_manager_test.cpp fuzz_test_util.cpp fuzzer_pass_add_useful_constructs_test.cpp + fuzzer_pass_construct_composites_test.cpp fuzzer_pass_donate_modules_test.cpp instruction_descriptor_test.cpp transformation_access_chain_test.cpp diff --git a/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/test/fuzz/fuzzer_pass_construct_composites_test.cpp new file mode 100644 index 0000000000..cc21f74df2 --- /dev/null +++ b/test/fuzz/fuzzer_pass_construct_composites_test.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_construct_composites.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) { + // This test declares various isomorphic structs, and a struct that is made up + // of these isomorphic structs. The pass to construct composites is then + // applied several times to check that no issues arise related to using a + // value of one struct type when a value of an isomorphic struct type is + // required. + + 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 = OpTypeFloat 32 + %7 = OpConstant %6 0 + %8 = OpTypeStruct %6 %6 %6 + %9 = OpTypeStruct %6 %6 %6 + %10 = OpTypeStruct %6 %6 %6 + %11 = OpTypeStruct %6 %6 %6 + %12 = OpTypeStruct %6 %6 %6 + %13 = OpTypeStruct %8 %9 %10 %11 %12 + %14 = OpConstantComposite %8 %7 %7 %7 + %15 = OpConstantComposite %9 %7 %7 %7 + %16 = OpConstantComposite %10 %7 %7 %7 + %17 = OpConstantComposite %11 %7 %7 %7 + %18 = OpConstantComposite %12 %7 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + + auto prng = MakeUnique(0); + + for (uint32_t i = 0; i < 10; i++) { + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + FuzzerContext fuzzer_context(prng.get(), 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassConstructComposites fuzzer_pass( + context.get(), &transformation_context, &fuzzer_context, + &transformation_sequence); + + fuzzer_pass.Apply(); + + // We just check that the result is valid. + ASSERT_TRUE(IsValid(env, context.get())); + } +} + +TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) { + // This test declares various isomorphic arrays, and a struct that is made up + // of these isomorphic arrays. The pass to construct composites is then + // applied several times to check that no issues arise related to using a + // value of one array type when a value of an isomorphic array type is + // required. + + 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 = OpTypeFloat 32 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 3 + %7 = OpConstant %6 0 + %8 = OpTypeArray %6 %51 + %9 = OpTypeArray %6 %51 + %10 = OpTypeArray %6 %51 + %11 = OpTypeArray %6 %51 + %12 = OpTypeArray %6 %51 + %13 = OpTypeStruct %8 %9 %10 %11 %12 + %14 = OpConstantComposite %8 %7 %7 %7 + %15 = OpConstantComposite %9 %7 %7 %7 + %16 = OpConstantComposite %10 %7 %7 %7 + %17 = OpConstantComposite %11 %7 %7 %7 + %18 = OpConstantComposite %12 %7 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + + auto prng = MakeUnique(0); + + for (uint32_t i = 0; i < 10; i++) { + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + FuzzerContext fuzzer_context(prng.get(), 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassConstructComposites fuzzer_pass( + context.get(), &transformation_context, &fuzzer_context, + &transformation_sequence); + + fuzzer_pass.Apply(); + + // We just check that the result is valid. + ASSERT_TRUE(IsValid(env, context.get())); + } +} + +} // namespace +} // namespace fuzz +} // namespace spvtools From e95fbfb1f509ad7a7fdfb72ac35fe412d72fc4a4 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Apr 2020 19:25:30 +0100 Subject: [PATCH 68/88] spirv-fuzz: Transformation to add OpConstantNull (#3273) Adds a transformation for adding OpConstantNull to a module, for appropriate data types. --- source/fuzz/CMakeLists.txt | 2 + source/fuzz/fuzzer_pass_donate_modules.cpp | 15 ++ source/fuzz/fuzzer_util.cpp | 7 + source/fuzz/fuzzer_util.h | 4 + source/fuzz/protobufs/spvtoolsfuzz.proto | 13 ++ source/fuzz/transformation.cpp | 4 + .../fuzz/transformation_add_constant_null.cpp | 66 +++++++++ .../fuzz/transformation_add_constant_null.h | 54 +++++++ test/fuzz/CMakeLists.txt | 1 + test/fuzz/fuzzer_pass_donate_modules_test.cpp | 66 +++++++++ .../transformation_add_constant_null_test.cpp | 140 ++++++++++++++++++ 11 files changed, 372 insertions(+) create mode 100644 source/fuzz/transformation_add_constant_null.cpp create mode 100644 source/fuzz/transformation_add_constant_null.h create mode 100644 test/fuzz/transformation_add_constant_null_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index cd53aea825..0f0581fbc8 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -79,6 +79,7 @@ if(SPIRV_BUILD_FUZZER) transformation_access_chain.h transformation_add_constant_boolean.h transformation_add_constant_composite.h + transformation_add_constant_null.h transformation_add_constant_scalar.h transformation_add_dead_block.h transformation_add_dead_break.h @@ -171,6 +172,7 @@ if(SPIRV_BUILD_FUZZER) transformation_access_chain.cpp transformation_add_constant_boolean.cpp transformation_add_constant_composite.cpp + transformation_add_constant_null.cpp transformation_add_constant_scalar.cpp transformation_add_dead_block.cpp transformation_add_dead_break.cpp diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 63ce7a64f4..4ba530526e 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -22,6 +22,7 @@ #include "source/fuzz/instruction_message.h" #include "source/fuzz/transformation_add_constant_boolean.h" #include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" #include "source/fuzz/transformation_add_constant_scalar.h" #include "source/fuzz/transformation_add_function.h" #include "source/fuzz/transformation_add_global_undef.h" @@ -394,6 +395,20 @@ void FuzzerPassDonateModules::HandleTypesAndValues( original_id_to_donated_id->at(type_or_value.type_id()), constituent_ids)); } break; + case SpvOpConstantNull: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the type associated with this null constant, so + // we cannot donate the null constant. + continue; + } + + // It is fine to have multiple OpConstantNull instructions of the same + // type, so we just add this to the recipient module. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantNull( + new_result_id, + original_id_to_donated_id->at(type_or_value.type_id()))); + } break; case SpvOpVariable: { // This is a global variable that could have one of various storage // classes. However, we change all global variable pointer storage diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 90cf9fe5cc..4d85984c5b 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -537,6 +537,13 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, return 0; } +bool IsNullConstantSupported(const opt::analysis::Type& type) { + return type.AsBool() || type.AsInteger() || type.AsFloat() || + type.AsMatrix() || type.AsVector() || type.AsArray() || + type.AsStruct() || type.AsPointer() || type.AsEvent() || + type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue(); +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 08edfc5869..886029ac31 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -210,6 +210,10 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, SpvStorageClass storage_class); +// Returns true if and only if |type| is one of the types for which it is legal +// to have an OpConstantNull value. +bool IsNullConstantSupported(const opt::analysis::Type& type); + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index b816e3b0ef..5dc70c3bfd 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -372,6 +372,7 @@ message Transformation { TransformationSwapCommutableOperands swap_commutable_operands = 41; TransformationPermuteFunctionParameters permute_function_parameters = 42; TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43; + TransformationAddConstantNull add_constant_null = 44; // Add additional option using the next available number. } } @@ -422,6 +423,18 @@ message TransformationAddConstantComposite { } +message TransformationAddConstantNull { + + // Adds a null constant. + + // Id for the constant + uint32 fresh_id = 1; + + // Type of the constant + uint32 type_id = 2; + +} + message TransformationAddConstantScalar { // Adds a constant of the given scalar type. diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index 6f008fcd04..40d2010e60 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -20,6 +20,7 @@ #include "source/fuzz/transformation_access_chain.h" #include "source/fuzz/transformation_add_constant_boolean.h" #include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" #include "source/fuzz/transformation_add_constant_scalar.h" #include "source/fuzz/transformation_add_dead_block.h" #include "source/fuzz/transformation_add_dead_break.h" @@ -78,6 +79,9 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kAddConstantComposite: return MakeUnique( message.add_constant_composite()); + case protobufs::Transformation::TransformationCase::kAddConstantNull: + return MakeUnique( + message.add_constant_null()); case protobufs::Transformation::TransformationCase::kAddConstantScalar: return MakeUnique( message.add_constant_scalar()); diff --git a/source/fuzz/transformation_add_constant_null.cpp b/source/fuzz/transformation_add_constant_null.cpp new file mode 100644 index 0000000000..dedbc2109a --- /dev/null +++ b/source/fuzz/transformation_add_constant_null.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_null.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantNull::TransformationAddConstantNull( + const spvtools::fuzz::protobufs::TransformationAddConstantNull& message) + : message_(message) {} + +TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id, + uint32_t type_id) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); +} + +bool TransformationAddConstantNull::IsApplicable( + opt::IRContext* context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + auto type = context->get_type_mgr()->GetType(message_.type_id()); + // The type must exist. + if (!type) { + return false; + } + // The type must be one of the types for which null constants are allowed, + // according to the SPIR-V spec. + return fuzzerutil::IsNullConstantSupported(*type); +} + +void TransformationAddConstantNull::Apply( + opt::IRContext* context, TransformationContext* /*unused*/) const { + context->module()->AddGlobalValue(MakeUnique( + context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList())); + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddConstantNull::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_constant_null() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_add_constant_null.h b/source/fuzz/transformation_add_constant_null.h new file mode 100644 index 0000000000..590fc0daa4 --- /dev/null +++ b/source/fuzz/transformation_add_constant_null.h @@ -0,0 +1,54 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantNull : public Transformation { + public: + explicit TransformationAddConstantNull( + const protobufs::TransformationAddConstantNull& message); + + TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a type for which it is acceptable + // to create a null constant + bool IsApplicable( + opt::IRContext* context, + const TransformationContext& transformation_context) const override; + + // Adds an OpConstantNull instruction to the module, with |message_.type_id| + // as its type. The instruction has result id |message_.fresh_id|. + void Apply(opt::IRContext* context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantNull message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index a8b09b9a8b..679a61b224 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -28,6 +28,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_access_chain_test.cpp transformation_add_constant_boolean_test.cpp transformation_add_constant_composite_test.cpp + transformation_add_constant_null_test.cpp transformation_add_constant_scalar_test.cpp transformation_add_dead_block_test.cpp transformation_add_dead_break_test.cpp diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index 549dd13fef..40d7d2466e 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -508,6 +508,72 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { ASSERT_TRUE(IsValid(env, recipient_context.get())); } +TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Private %6 + %8 = OpConstantNull %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { std::string recipient_shader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_constant_null_test.cpp b/test/fuzz/transformation_add_constant_null_test.cpp new file mode 100644 index 0000000000..0bfee34b77 --- /dev/null +++ b/test/fuzz/transformation_add_constant_null_test.cpp @@ -0,0 +1,140 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_null.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddConstantNullTest, BasicTest) { + 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 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %20 = OpTypeSampler + %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f + %22 = OpTypeSampledImage %21 + %4 = OpFunction %2 None %3 + %5 = 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; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + // Id already in use + ASSERT_FALSE(TransformationAddConstantNull(4, 11).IsApplicable( + context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddConstantNull(100, 1).IsApplicable( + context.get(), transformation_context)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddConstantNull(100, 3).IsApplicable( + context.get(), transformation_context)); + + // %20 is a sampler type + ASSERT_FALSE(TransformationAddConstantNull(100, 20).IsApplicable( + context.get(), transformation_context)); + + // %21 is an image type + ASSERT_FALSE(TransformationAddConstantNull(100, 21).IsApplicable( + context.get(), transformation_context)); + + // %22 is a sampled image type + ASSERT_FALSE(TransformationAddConstantNull(100, 22).IsApplicable( + context.get(), transformation_context)); + + TransformationAddConstantNull transformations[] = { + // %100 = OpConstantNull %6 + TransformationAddConstantNull(100, 6), + + // %101 = OpConstantNull %7 + TransformationAddConstantNull(101, 7), + + // %102 = OpConstantNull %8 + TransformationAddConstantNull(102, 8), + + // %103 = OpConstantNull %9 + TransformationAddConstantNull(103, 9), + + // %104 = OpConstantNull %10 + TransformationAddConstantNull(104, 10), + + // %105 = OpConstantNull %11 + TransformationAddConstantNull(105, 11)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + } + 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %20 = OpTypeSampler + %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f + %22 = OpTypeSampledImage %21 + %100 = OpConstantNull %6 + %101 = OpConstantNull %7 + %102 = OpConstantNull %8 + %103 = OpConstantNull %9 + %104 = OpConstantNull %10 + %105 = OpConstantNull %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools From 4af38c49bfeeac536b9bf9b25be2d4bbec356972 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 6 Apr 2020 16:08:14 +0100 Subject: [PATCH 69/88] spirv-fuzz: Improve support for compute shaders in donation (#3277) (1) Runtime arrays are turned into fixed-size arrays, by turning OpTypeRuntimeArray into OpTypeArray and uses of OpArrayLength into uses of the constant used for the length of the fixed-size array. (2) Atomic instructions are not donated, and uses of their results are replaced with uses of constants of the result type. --- .../fuzz/fuzzer_pass_add_function_calls.cpp | 24 +- .../fuzz/fuzzer_pass_add_global_variables.cpp | 4 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 234 +++++++++-- source/fuzz/fuzzer_pass_donate_modules.h | 6 + source/fuzz/protobufs/spvtoolsfuzz.proto | 11 +- source/fuzz/transformation_add_function.cpp | 6 + .../transformation_add_global_variable.cpp | 61 ++- .../fuzz/transformation_add_global_variable.h | 23 +- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 374 +++++++++++++++++- ...ransformation_add_global_variable_test.cpp | 150 +++++-- 10 files changed, 784 insertions(+), 109 deletions(-) diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp index f666eb25ad..569df103ec 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -214,8 +214,9 @@ std::vector FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments( result.push_back(fresh_variable_id); // Now bring the variable into existence. - if (type_instruction->GetSingleWordInOperand(0) == - SpvStorageClassFunction) { + auto storage_class = static_cast( + type_instruction->GetSingleWordInOperand(0)); + if (storage_class == SpvStorageClassFunction) { // Add a new zero-initialized local variable to the current // function, noting that its pointee value is irrelevant. ApplyTransformation(TransformationAddLocalVariable( @@ -224,16 +225,19 @@ std::vector FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments( type_instruction->GetSingleWordInOperand(1)), true)); } else { - assert(type_instruction->GetSingleWordInOperand(0) == - SpvStorageClassPrivate && - "Only Function and Private storage classes are " + assert((storage_class == SpvStorageClassPrivate || + storage_class == SpvStorageClassWorkgroup) && + "Only Function, Private and Workgroup storage classes are " "supported at present."); - // Add a new zero-initialized global variable to the module, - // noting that its pointee value is irrelevant. + // Add a new global variable to the module, zero-initializing it if + // it has Private storage class, and noting that its pointee value is + // irrelevant. ApplyTransformation(TransformationAddGlobalVariable( - fresh_variable_id, arg_type_id, - FindOrCreateZeroConstant( - type_instruction->GetSingleWordInOperand(1)), + fresh_variable_id, arg_type_id, storage_class, + storage_class == SpvStorageClassPrivate + ? FindOrCreateZeroConstant( + type_instruction->GetSingleWordInOperand(1)) + : 0, true)); } } else { diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index 80708edfb8..4023b22079 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -66,9 +66,11 @@ void FuzzerPassAddGlobalVariables::Apply() { available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( available_pointers_to_basic_type)]; } + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3274): We could + // add new variables with Workgroup storage class in compute shaders. ApplyTransformation(TransformationAddGlobalVariable( GetFuzzerContext()->GetFreshId(), pointer_type_id, - FindOrCreateZeroConstant(basic_type), true)); + SpvStorageClassPrivate, FindOrCreateZeroConstant(basic_type), true)); } } diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 4ba530526e..b043b7f5f3 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -116,6 +116,7 @@ SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass( switch (donor_storage_class) { case SpvStorageClassFunction: case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: // We leave these alone return donor_storage_class; case SpvStorageClassInput: @@ -280,36 +281,51 @@ void FuzzerPassDonateModules::HandleTypesAndValues( // It is OK to have multiple structurally identical array types, so // we go ahead and add a remapped version of the type declared by the // donor. + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); new_result_id = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationAddTypeArray( - new_result_id, - original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(0)), + new_result_id, original_id_to_donated_id->at(component_type_id), original_id_to_donated_id->at( type_or_value.GetSingleWordInOperand(1)))); } break; + case SpvOpTypeRuntimeArray: { + // A runtime array is allowed as the final member of an SSBO. During + // donation we turn runtime arrays into fixed-size arrays. For dead + // code donations this is OK because the array is never indexed into at + // runtime, so it does not matter what its size is. For live-safe code, + // all accesses are made in-bounds, so this is also OK. + // + // The special OpArrayLength instruction, which works on runtime arrays, + // is rewritten to yield the fixed length that is used for the array. + + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeArray( + new_result_id, original_id_to_donated_id->at(component_type_id), + FindOrCreate32BitIntegerConstant( + GetFuzzerContext()->GetRandomSizeForNewArray(), false))); + } break; case SpvOpTypeStruct: { // Similar to SpvOpTypeArray. - new_result_id = GetFuzzerContext()->GetFreshId(); std::vector member_type_ids; - type_or_value.ForEachInId( - [&member_type_ids, - &original_id_to_donated_id](const uint32_t* component_type_id) { - member_type_ids.push_back( - original_id_to_donated_id->at(*component_type_id)); - }); + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + auto component_type_id = type_or_value.GetSingleWordInOperand(i); + member_type_ids.push_back( + original_id_to_donated_id->at(component_type_id)); + } + new_result_id = GetFuzzerContext()->GetFreshId(); ApplyTransformation( TransformationAddTypeStruct(new_result_id, member_type_ids)); } break; case SpvOpTypePointer: { // Similar to SpvOpTypeArray. + uint32_t pointee_type_id = type_or_value.GetSingleWordInOperand(1); new_result_id = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationAddTypePointer( new_result_id, AdaptStorageClass(static_cast( type_or_value.GetSingleWordInOperand(0))), - original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(1)))); + original_id_to_donated_id->at(pointee_type_id))); } break; case SpvOpTypeFunction: { // It is not OK to have multiple function types that use identical ids @@ -333,8 +349,10 @@ void FuzzerPassDonateModules::HandleTypesAndValues( std::vector return_and_parameter_types; for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { - return_and_parameter_types.push_back(original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(i))); + uint32_t return_or_parameter_type = + type_or_value.GetSingleWordInOperand(i); + return_and_parameter_types.push_back( + original_id_to_donated_id->at(return_or_parameter_type)); } uint32_t existing_function_id = fuzzerutil::FindFunctionType( GetIRContext(), return_and_parameter_types); @@ -379,6 +397,10 @@ void FuzzerPassDonateModules::HandleTypesAndValues( data_words)); } break; case SpvOpConstantComposite: { + assert(original_id_to_donated_id->count(type_or_value.type_id()) && + "Composite types for which it is possible to create a constant " + "should have been donated."); + // It is OK to have duplicate constant composite definitions, so add // this to the module using remapped versions of all consituent ids and // the result type. @@ -387,6 +409,9 @@ void FuzzerPassDonateModules::HandleTypesAndValues( type_or_value.ForEachInId( [&constituent_ids, &original_id_to_donated_id](const uint32_t* constituent_id) { + assert(original_id_to_donated_id->count(*constituent_id) && + "The constants used to construct this composite should " + "have been donated."); constituent_ids.push_back( original_id_to_donated_id->at(*constituent_id)); }); @@ -396,12 +421,6 @@ void FuzzerPassDonateModules::HandleTypesAndValues( constituent_ids)); } break; case SpvOpConstantNull: { - if (!original_id_to_donated_id->count(type_or_value.type_id())) { - // We did not donate the type associated with this null constant, so - // we cannot donate the null constant. - continue; - } - // It is fine to have multiple OpConstantNull instructions of the same // type, so we just add this to the recipient module. new_result_id = GetFuzzerContext()->GetFreshId(); @@ -413,10 +432,14 @@ void FuzzerPassDonateModules::HandleTypesAndValues( // This is a global variable that could have one of various storage // classes. However, we change all global variable pointer storage // classes (such as Uniform, Input and Output) to private when donating - // pointer types. Thus this variable's pointer type is guaranteed to - // have storage class private. As a result, we simply add a Private - // storage class global variable, using remapped versions of the result - // type and initializer ids for the global variable in the donor. + // pointer types, with the exception of the Workgroup storage class. + // + // Thus this variable's pointer type is guaranteed to have storage class + // Private or Workgroup. + // + // We add a global variable with either Private or Workgroup storage + // class, using remapped versions of the result type and initializer ids + // for the global variable in the donor. // // We regard the added variable as having an irrelevant value. This // means that future passes can add stores to the variable in any @@ -426,19 +449,35 @@ void FuzzerPassDonateModules::HandleTypesAndValues( uint32_t remapped_pointer_type = original_id_to_donated_id->at(type_or_value.type_id()); uint32_t initializer_id; + SpvStorageClass storage_class = + static_cast(type_or_value.GetSingleWordInOperand( + 0)) == SpvStorageClassWorkgroup + ? SpvStorageClassWorkgroup + : SpvStorageClassPrivate; if (type_or_value.NumInOperands() == 1) { - // The variable did not have an initializer; initialize it to zero. - // This is to limit problems associated with uninitialized data. - initializer_id = FindOrCreateZeroConstant( - fuzzerutil::GetPointeeTypeIdFromPointerType( - GetIRContext(), remapped_pointer_type)); + // The variable did not have an initializer. Initialize it to zero + // if it has Private storage class (to limit problems associated with + // uninitialized data), and leave it uninitialized if it has Workgroup + // storage class (as Workgroup variables cannot have initializers). + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3275): we + // could initialize Workgroup variables at the start of an entry + // point, and should do so if their uninitialized nature proves + // problematic. + initializer_id = + storage_class == SpvStorageClassWorkgroup + ? 0 + : FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), remapped_pointer_type)); } else { // The variable already had an initializer; use its remapped id. initializer_id = original_id_to_donated_id->at( type_or_value.GetSingleWordInOperand(1)); } ApplyTransformation(TransformationAddGlobalVariable( - new_result_id, remapped_pointer_type, initializer_id, true)); + new_result_id, remapped_pointer_type, storage_class, initializer_id, + true)); } break; case SpvOpUndef: { // It is fine to have multiple Undef instructions of the same type, so @@ -493,9 +532,80 @@ void FuzzerPassDonateModules::HandleFunctions( // Scan through the function, remapping each result id that it generates to // a fresh id. This is necessary because functions include forward // references, e.g. to labels. - function_to_donate->ForEachInst([this, &original_id_to_donated_id]( + function_to_donate->ForEachInst([this, donor_ir_context, + &original_id_to_donated_id]( const opt::Instruction* instruction) { - if (instruction->result_id()) { + if (!instruction->result_id()) { + return; + } + if (IgnoreInstruction(instruction)) { + if (instruction->opcode() == SpvOpArrayLength) { + // We treat the OpArrayLength instruction specially. In the donor + // shader this gets the length of a runtime array that is the final + // member of a struct. During donation, we will have converted the + // runtime array type, and the associated struct field, into a fixed- + // size array. We can then use the constant size of this fixed-sized + // array wherever we would have used the result of an OpArrayLength + // instruction. + uint32_t donated_variable_id = original_id_to_donated_id->at( + instruction->GetSingleWordInOperand(0)); + auto donated_variable_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(donated_variable_id); + auto pointer_to_struct_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + donated_variable_instruction->type_id()); + assert(pointer_to_struct_instruction->opcode() == SpvOpTypePointer && + "Type of variable must be pointer."); + auto donated_struct_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + pointer_to_struct_instruction->GetSingleWordInOperand(1)); + assert( + donated_struct_type_instruction->opcode() == SpvOpTypeStruct && + "Pointee type of pointer used by OpArrayLength must be struct."); + assert(donated_struct_type_instruction->NumInOperands() == + instruction->GetSingleWordInOperand(1) + 1 && + "OpArrayLength must refer to the final member of the given " + "struct."); + uint32_t fixed_size_array_type_id = + donated_struct_type_instruction->GetSingleWordInOperand( + donated_struct_type_instruction->NumInOperands() - 1); + auto fixed_size_array_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + fixed_size_array_type_id); + assert(fixed_size_array_type_instruction->opcode() == + SpvOpTypeArray && + "The donated array type must be fixed-size."); + auto array_size_id = + fixed_size_array_type_instruction->GetSingleWordInOperand(1); + original_id_to_donated_id->insert( + {instruction->result_id(), array_size_id}); + } else if (instruction->type_id()) { + // If the ignored instruction has a basic result type then we + // associate its result id with a constant of that type, so that + // instructions that use the result id will use the constant instead. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): + // Using this particular constant is arbitrary, so if we have a + // mechanism for noting that an id use is arbitrary and could be + // fuzzed we should use it here. + auto type_inst = donor_ir_context->get_def_use_mgr()->GetDef( + instruction->type_id()); + switch (type_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeStruct: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + original_id_to_donated_id->insert( + {instruction->result_id(), + FindOrCreateZeroConstant( + original_id_to_donated_id->at(instruction->type_id()))}); + default: + break; + } + } + } else { original_id_to_donated_id->insert( {instruction->result_id(), GetFuzzerContext()->GetFreshId()}); } @@ -505,6 +615,10 @@ void FuzzerPassDonateModules::HandleFunctions( function_to_donate->ForEachInst([this, &donated_instructions, &original_id_to_donated_id]( const opt::Instruction* instruction) { + if (IgnoreInstruction(instruction)) { + return; + } + // Get the instruction's input operands into donation-ready form, // remapping any id uses in the process. opt::Instruction::OperandList input_operands; @@ -642,9 +756,28 @@ void FuzzerPassDonateModules::HandleFunctions( GetFuzzerContext()->GetFreshId()); // Get the bound for the component being indexed into. - uint32_t bound = - TransformationAddFunction::GetBoundForCompositeIndex( - donor_ir_context, *should_be_composite_type); + uint32_t bound; + if (should_be_composite_type->opcode() == + SpvOpTypeRuntimeArray) { + // The donor is indexing into a runtime array. We do not + // donate runtime arrays. Instead, we donate a corresponding + // fixed-size array for every runtime array. We should thus + // find that donor composite type's result id maps to a fixed- + // size array. + auto fixed_size_array_type = + GetIRContext()->get_def_use_mgr()->GetDef( + original_id_to_donated_id->at( + should_be_composite_type->result_id())); + assert(fixed_size_array_type->opcode() == SpvOpTypeArray && + "A runtime array type in the donor should have been " + "replaced by a fixed-sized array in the recipient."); + // The size of this fixed-size array is a suitable bound. + bound = TransformationAddFunction::GetBoundForCompositeIndex( + GetIRContext(), *fixed_size_array_type); + } else { + bound = TransformationAddFunction::GetBoundForCompositeIndex( + donor_ir_context, *should_be_composite_type); + } const uint32_t index_id = inst.GetSingleWordInOperand(index); auto index_inst = donor_ir_context->get_def_use_mgr()->GetDef(index_id); @@ -707,6 +840,37 @@ void FuzzerPassDonateModules::HandleFunctions( } } +bool FuzzerPassDonateModules::IgnoreInstruction( + const opt::Instruction* instruction) { + switch (instruction->opcode()) { + case SpvOpArrayLength: + // We ignore instructions that get the length of runtime arrays, because + // we turn all runtime arrays into fixed-size arrays. + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + // We conservatively ignore all atomic instructions at present. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3276): Consider + // being less conservative here. + return true; + default: + return false; + } +} + std::vector FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder( opt::IRContext* context) { diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index 9087daf232..909f3bc4ed 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -77,6 +77,12 @@ class FuzzerPassDonateModules : public FuzzerPass { std::map* original_id_to_donated_id, bool make_livesafe); + // During donation we will have to ignore some instructions, e.g. because they + // use opcodes that we cannot support or because they reference the ids of + // instructions that have not been donated. This function encapsulates the + // logic for deciding which instructions should be ignored. + bool IgnoreInstruction(const opt::Instruction* instruction); + // Returns the ids of all functions in |context| in a topological order in // relation to the call graph of |context|, which is assumed to be recursion- // free. diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 5dc70c3bfd..2af3a92c9e 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -560,8 +560,9 @@ message TransformationAddGlobalUndef { message TransformationAddGlobalVariable { - // Adds a global variable of the given type to the module, with Private - // storage class and optionally with an initializer. + // Adds a global variable of the given type to the module, with Private or + // Workgroup storage class, and optionally (for the Private case) with an + // initializer. // Fresh id for the global variable uint32 fresh_id = 1; @@ -569,13 +570,15 @@ message TransformationAddGlobalVariable { // The type of the global variable uint32 type_id = 2; + uint32 storage_class = 3; + // Initial value of the variable - uint32 initializer_id = 3; + uint32 initializer_id = 4; // True if and only if the behaviour of the module should not depend on the // value of the variable, in which case stores to the variable can be // performed in an arbitrary fashion. - bool value_is_irrelevant = 4; + bool value_is_irrelevant = 5; } diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp index 45fe342d64..c990f23683 100644 --- a/source/fuzz/transformation_add_function.cpp +++ b/source/fuzz/transformation_add_function.cpp @@ -897,6 +897,11 @@ uint32_t TransformationAddFunction::GetBoundForCompositeIndex( case SpvOpTypeStruct: { return fuzzerutil::GetNumberOfStructMembers(composite_type_inst); } + case SpvOpTypeRuntimeArray: + assert(false && + "GetBoundForCompositeIndex should not be invoked with an " + "OpTypeRuntimeArray, which does not have a static bound."); + return 0; default: assert(false && "Unknown composite type."); return 0; @@ -909,6 +914,7 @@ opt::Instruction* TransformationAddFunction::FollowCompositeIndex( uint32_t sub_object_type_id; switch (composite_type_inst.opcode()) { case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); break; case SpvOpTypeMatrix: diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp index c0164280de..6464bfbb90 100644 --- a/source/fuzz/transformation_add_global_variable.cpp +++ b/source/fuzz/transformation_add_global_variable.cpp @@ -24,10 +24,11 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable( : message_(message) {} TransformationAddGlobalVariable::TransformationAddGlobalVariable( - uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id, - bool value_is_irrelevant) { + uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class, + uint32_t initializer_id, bool value_is_irrelevant) { message_.set_fresh_id(fresh_id); message_.set_type_id(type_id); + message_.set_storage_class(storage_class); message_.set_initializer_id(initializer_id); message_.set_value_is_irrelevant(value_is_irrelevant); } @@ -38,6 +39,17 @@ bool TransformationAddGlobalVariable::IsApplicable( if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } + + // The storage class must be Private or Workgroup. + auto storage_class = static_cast(message_.storage_class()); + switch (storage_class) { + case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: + break; + default: + assert(false && "Unsupported storage class."); + return false; + } // The type id must correspond to a type. auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); if (!type) { @@ -48,23 +60,32 @@ bool TransformationAddGlobalVariable::IsApplicable( if (!pointer_type) { return false; } - // ... with Private storage class. - if (pointer_type->storage_class() != SpvStorageClassPrivate) { + // ... with the right storage class. + if (pointer_type->storage_class() != storage_class) { return false; } - // The initializer id must be the id of a constant. Check this with the - // constant manager. - auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds( - {message_.initializer_id()}); - if (constant_id.empty()) { - return false; - } - assert(constant_id.size() == 1 && - "We asked for the constant associated with a single id; we should " - "get a single constant."); - // The type of the constant must match the pointee type of the pointer. - if (pointer_type->pointee_type() != constant_id[0]->type()) { - return false; + if (message_.initializer_id()) { + // An initializer is not allowed if the storage class is Workgroup. + if (storage_class == SpvStorageClassWorkgroup) { + assert(false && + "By construction this transformation should not have an " + "initializer when Workgroup storage class is used."); + return false; + } + // The initializer id must be the id of a constant. Check this with the + // constant manager. + auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds( + {message_.initializer_id()}); + if (constant_id.empty()) { + return false; + } + assert(constant_id.size() == 1 && + "We asked for the constant associated with a single id; we should " + "get a single constant."); + // The type of the constant must match the pointee type of the pointer. + if (pointer_type->pointee_type() != constant_id[0]->type()) { + return false; + } } return true; } @@ -74,7 +95,7 @@ void TransformationAddGlobalVariable::Apply( TransformationContext* transformation_context) const { opt::Instruction::OperandList input_operands; input_operands.push_back( - {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}}); + {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}}); if (message_.initializer_id()) { input_operands.push_back( {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}); @@ -84,7 +105,7 @@ void TransformationAddGlobalVariable::Apply( input_operands)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(ir_context)) { + if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(ir_context)) { // Conservatively add this global to the interface of every entry point in // the module. This means that the global is available for other // transformations to use. @@ -117,7 +138,7 @@ protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const { } bool TransformationAddGlobalVariable:: - PrivateGlobalsMustBeDeclaredInEntryPointInterfaces( + GlobalVariablesMustBeDeclaredInEntryPointInterfaces( opt::IRContext* ir_context) { // TODO(afd): We capture the universal environments for which this requirement // holds. The check should be refined on demand for other target diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h index f28af445cd..289af9e9ba 100644 --- a/source/fuzz/transformation_add_global_variable.h +++ b/source/fuzz/transformation_add_global_variable.h @@ -29,22 +29,26 @@ class TransformationAddGlobalVariable : public Transformation { const protobufs::TransformationAddGlobalVariable& message); TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id, + SpvStorageClass storage_class, uint32_t initializer_id, bool value_is_irrelevant); // - |message_.fresh_id| must be fresh - // - |message_.type_id| must be the id of a pointer type with Private storage - // class - // - |message_.initializer_id| must either be 0 or the id of a constant whose + // - |message_.type_id| must be the id of a pointer type with the same storage + // class as |message_.storage_class| + // - |message_.storage_class| must be Private or Workgroup + // - |message_.initializer_id| must be 0 if |message_.storage_class| is + // Workgroup, and otherwise may either be 0 or the id of a constant whose // type is the pointee type of |message_.type_id| bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; - // Adds a global variable with Private storage class to the module, with type - // |message_.type_id| and either no initializer or |message_.initializer_id| - // as an initializer, depending on whether |message_.initializer_id| is 0. - // The global variable has result id |message_.fresh_id|. + // Adds a global variable with storage class |message_.storage_class| to the + // module, with type |message_.type_id| and either no initializer or + // |message_.initializer_id| as an initializer, depending on whether + // |message_.initializer_id| is 0. The global variable has result id + // |message_.fresh_id|. // // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the // fact manager in |transformation_context|. @@ -54,7 +58,10 @@ class TransformationAddGlobalVariable : public Transformation { protobufs::Transformation ToMessage() const override; private: - static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces( + // Returns true if and only if the SPIR-V version being used requires that + // global variables accessed in the static call graph of an entry point need + // to be listed in that entry point's interface. + static bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( opt::IRContext* ir_context); protobufs::TransformationAddGlobalVariable message_; diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index 40d7d2466e..bbc92b92be 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -198,8 +198,8 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) { TransformationContext transformation_context(&fact_manager, validator_options); - auto prng = MakeUnique(0); - FuzzerContext fuzzer_context(prng.get(), 100); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -276,7 +276,8 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { TransformationContext transformation_context(&fact_manager, validator_options); - FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -403,7 +404,8 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { TransformationContext transformation_context(&fact_manager, validator_options); - FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -560,7 +562,92 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { TransformationContext transformation_context(&fact_manager, validator_options); - FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %9 ArrayStride 4 + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 BufferBlock + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeRuntimeArray %6 + %10 = OpTypeStruct %9 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %13 = OpTypeInt 32 0 + %16 = OpConstant %6 0 + %18 = OpConstant %6 1 + %20 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %14 = OpArrayLength %13 %12 0 + %15 = OpBitcast %6 %14 + OpStore %8 %15 + %17 = OpLoad %6 %8 + %19 = OpISub %6 %17 %18 + %21 = OpAccessChain %20 %12 %16 %19 + OpStore %21 %16 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -574,6 +661,283 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { ASSERT_TRUE(IsValid(env, recipient_context.get())); } +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %16 ArrayStride 4 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpTypeRuntimeArray %6 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 0 + %23 = OpTypeBool + %26 = OpConstant %6 32 + %27 = OpTypePointer Uniform %6 + %30 = 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 + %21 = OpArrayLength %20 %19 0 + %22 = OpBitcast %6 %21 + %24 = OpSLessThan %23 %15 %22 + OpBranchConditional %24 %11 %12 + %11 = OpLabel + %25 = OpLoad %6 %8 + %28 = OpAccessChain %27 %19 %9 %25 + OpStore %28 %26 + OpBranch %13 + %13 = OpLabel + %29 = OpLoad %6 %8 + %31 = OpIAdd %6 %29 %30 + OpStore %8 %31 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Workgroup %6 + %8 = OpVariable %7 Workgroup + %9 = OpConstant %6 2 + %10 = OpVariable %7 Workgroup + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpStore %10 %11 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpTypePointer Uniform %6 + %16 = OpConstant %6 1 + %17 = OpConstant %6 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %15 = OpAccessChain %14 %11 %13 + %18 = OpAtomicIAdd %6 %15 %16 %17 %16 + OpStore %8 %18 + %19 = OpAccessChain %14 %11 %13 + %20 = OpLoad %6 %8 + %21 = OpAtomicUMin %6 %19 %16 %17 %20 + OpStore %8 %21 + %22 = OpAccessChain %14 %11 %13 + %23 = OpLoad %6 %8 + %24 = OpAtomicUMax %6 %22 %16 %17 %23 + OpStore %8 %24 + %25 = OpAccessChain %14 %11 %13 + %26 = OpLoad %6 %8 + %27 = OpAtomicAnd %6 %25 %16 %17 %26 + OpStore %8 %27 + %28 = OpAccessChain %14 %11 %13 + %29 = OpLoad %6 %8 + %30 = OpAtomicOr %6 %28 %16 %17 %29 + OpStore %8 %30 + %31 = OpAccessChain %14 %11 %13 + %32 = OpLoad %6 %8 + %33 = OpAtomicXor %6 %31 %16 %17 %32 + OpStore %8 %33 + %34 = OpAccessChain %14 %11 %13 + %35 = OpLoad %6 %8 + %36 = OpAtomicExchange %6 %34 %16 %17 %35 + OpStore %8 %36 + %37 = OpAccessChain %14 %11 %13 + %38 = OpLoad %6 %8 + %39 = OpAtomicCompareExchange %6 %37 %16 %17 %17 %16 %38 + OpStore %8 %39 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { std::string recipient_shader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp index 9b8faa4c42..5c74ca0633 100644 --- a/test/fuzz/transformation_add_global_variable_test.cpp +++ b/test/fuzz/transformation_add_global_variable_test.cpp @@ -65,66 +65,82 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { validator_options); // Id already in use - ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0, true) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddGlobalVariable(4, 10, SpvStorageClassPrivate, 0, true) + .IsApplicable(context.get(), transformation_context)); // %1 is not a type - ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0, false) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 1, SpvStorageClassPrivate, 0, false) + .IsApplicable(context.get(), transformation_context)); // %7 is not a pointer type - ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0, true) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 7, SpvStorageClassPrivate, 0, true) + .IsApplicable(context.get(), transformation_context)); // %9 does not have Private storage class - ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0, false) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 9, SpvStorageClassPrivate, 0, false) + .IsApplicable(context.get(), transformation_context)); // %15 does not have Private storage class - ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0, true) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 15, SpvStorageClassPrivate, 0, true) + .IsApplicable(context.get(), transformation_context)); // %10 is a pointer to float, while %16 is an int constant - ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16, false) + ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, + 16, false) .IsApplicable(context.get(), transformation_context)); // %10 is a Private pointer to float, while %15 is a variable with type // Uniform float pointer - ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15, true) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, 15, true) + .IsApplicable(context.get(), transformation_context)); // %12 is a Private pointer to int, while %10 is a variable with type // Private float pointer - ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10, false) + ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, + 10, false) .IsApplicable(context.get(), transformation_context)); // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK // since the initializer's type should be the *pointee* type. - ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14, true) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddGlobalVariable(104, 10, SpvStorageClassPrivate, 14, true) + .IsApplicable(context.get(), transformation_context)); // This would work in principle, but logical addressing does not allow // a pointer to a pointer. - ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14, false) + ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, SpvStorageClassPrivate, + 14, false) .IsApplicable(context.get(), transformation_context)); TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, 16, true), + TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, + true), // %101 = OpVariable %10 Private - TransformationAddGlobalVariable(101, 10, 40, false), + TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40, + false), // %102 = OpVariable %13 Private - TransformationAddGlobalVariable(102, 13, 41, true), + TransformationAddGlobalVariable(102, 13, SpvStorageClassPrivate, 41, + true), // %103 = OpVariable %12 Private %16 - TransformationAddGlobalVariable(103, 12, 16, false), + TransformationAddGlobalVariable(103, 12, SpvStorageClassPrivate, 16, + false), // %104 = OpVariable %19 Private %21 - TransformationAddGlobalVariable(104, 19, 21, true), + TransformationAddGlobalVariable(104, 19, SpvStorageClassPrivate, 21, + true), // %105 = OpVariable %19 Private %22 - TransformationAddGlobalVariable(105, 19, 22, false)}; + TransformationAddGlobalVariable(105, 19, SpvStorageClassPrivate, 22, + false)}; for (auto& transformation : transformations) { ASSERT_TRUE( @@ -239,13 +255,16 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { TransformationAddGlobalVariable transformations[] = { // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, 16, true), + TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, + true), // %101 = OpVariable %12 Private %16 - TransformationAddGlobalVariable(101, 12, 16, false), + TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16, + false), // %102 = OpVariable %19 Private %21 - TransformationAddGlobalVariable(102, 19, 21, true)}; + TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21, + true)}; for (auto& transformation : transformations) { ASSERT_TRUE( @@ -301,6 +320,85 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) { + // This checks that workgroup globals can be added to a compute shader. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Workgroup %6 + %50 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = 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; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + +#ifndef NDEBUG + ASSERT_DEATH( + TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 50, true) + .IsApplicable(context.get(), transformation_context), + "By construction this transformation should not have an.*initializer " + "when Workgroup storage class is used"); +#endif + + TransformationAddGlobalVariable transformations[] = { + // %8 = OpVariable %7 Workgroup + TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 0, true), + + // %10 = OpVariable %7 Workgroup + TransformationAddGlobalVariable(10, 7, SpvStorageClassWorkgroup, 0, + false)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + } + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(10)); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %8 %10 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Workgroup %6 + %50 = OpConstant %6 2 + %8 = OpVariable %7 Workgroup + %10 = OpVariable %7 Workgroup + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools From 183e3242a361bb86eaf65be68366cfde72e80d12 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 7 Apr 2020 17:37:51 +0100 Subject: [PATCH 70/88] spirv-fuzz: Handle more general SPIR-V in donation (#3280) This change increases the extent to which arbitrary SPIR-V can be used by the fuzzer pass that donates modules. It handles the case where various ingredients (such as types, variables and particular instructions) cannot be donated by omitting them, and then either omitting their dependencies or replacing their dependencies with alternative instructions. The change pays particular attention to allowing code that manipulates image types to be handled (by skipping anything image-specific). --- source/fuzz/fuzzer_pass_donate_modules.cpp | 1455 ++++++++++------- source/fuzz/fuzzer_pass_donate_modules.h | 65 +- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 483 +++++- 3 files changed, 1376 insertions(+), 627 deletions(-) diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index b043b7f5f3..bb48303ef9 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -167,336 +167,385 @@ void FuzzerPassDonateModules::HandleTypesAndValues( std::map* original_id_to_donated_id) { // Consider every type/global/constant/undef in the module. for (auto& type_or_value : donor_ir_context->module()->types_values()) { - // Each such instruction generates a result id, and as part of donation we - // need to associate the donor's result id with a new result id. That new - // result id will either be the id of some existing instruction, or a fresh - // id. This variable captures it. - uint32_t new_result_id; + HandleTypeOrValue(type_or_value, original_id_to_donated_id); + } +} - // Decide how to handle each kind of instruction on a case-by-case basis. - // - // Because the donor module is required to be valid, when we encounter a - // type comprised of component types (e.g. an aggregate or pointer), we know - // that its component types will have been considered previously, and that - // |original_id_to_donated_id| will already contain an entry for them. - switch (type_or_value.opcode()) { - case SpvOpTypeVoid: { - // Void has to exist already in order for us to have an entry point. - // Get the existing id of void. - opt::analysis::Void void_type; - new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type); - assert(new_result_id && - "The module being transformed will always have 'void' type " - "declared."); - } break; - case SpvOpTypeBool: { - // Bool cannot be declared multiple times, so use its existing id if - // present, or add a declaration of Bool with a fresh id if not. - opt::analysis::Bool bool_type; - auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type); - if (bool_type_id) { - new_result_id = bool_type_id; - } else { - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddTypeBoolean(new_result_id)); - } - } break; - case SpvOpTypeInt: { - // Int cannot be declared multiple times with the same width and - // signedness, so check whether an existing identical Int type is - // present and use its id if so. Otherwise add a declaration of the - // Int type used by the donor, with a fresh id. - const uint32_t width = type_or_value.GetSingleWordInOperand(0); - const bool is_signed = - static_cast(type_or_value.GetSingleWordInOperand(1)); - opt::analysis::Integer int_type(width, is_signed); - auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type); - if (int_type_id) { - new_result_id = int_type_id; - } else { - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation( - TransformationAddTypeInt(new_result_id, width, is_signed)); - } - } break; - case SpvOpTypeFloat: { - // Similar to SpvOpTypeInt. - const uint32_t width = type_or_value.GetSingleWordInOperand(0); - opt::analysis::Float float_type(width); - auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type); - if (float_type_id) { - new_result_id = float_type_id; - } else { - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddTypeFloat(new_result_id, width)); - } - } break; - case SpvOpTypeVector: { - // It is not legal to have two Vector type declarations with identical - // element types and element counts, so check whether an existing - // identical Vector type is present and use its id if so. Otherwise add - // a declaration of the Vector type used by the donor, with a fresh id. - - // When considering the vector's component type id, we look up the id - // use in the donor to find the id to which this has been remapped. - uint32_t component_type_id = original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(0)); - auto component_type = - GetIRContext()->get_type_mgr()->GetType(component_type_id); - assert(component_type && "The base type should be registered."); - auto component_count = type_or_value.GetSingleWordInOperand(1); - opt::analysis::Vector vector_type(component_type, component_count); - auto vector_type_id = - GetIRContext()->get_type_mgr()->GetId(&vector_type); - if (vector_type_id) { - new_result_id = vector_type_id; - } else { - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddTypeVector( - new_result_id, component_type_id, component_count)); - } - } break; - case SpvOpTypeMatrix: { - // Similar to SpvOpTypeVector. - uint32_t column_type_id = original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(0)); - auto column_type = - GetIRContext()->get_type_mgr()->GetType(column_type_id); - assert(column_type && column_type->AsVector() && - "The column type should be a registered vector type."); - auto column_count = type_or_value.GetSingleWordInOperand(1); - opt::analysis::Matrix matrix_type(column_type, column_count); - auto matrix_type_id = - GetIRContext()->get_type_mgr()->GetId(&matrix_type); - if (matrix_type_id) { - new_result_id = matrix_type_id; - } else { - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddTypeMatrix( - new_result_id, column_type_id, column_count)); - } +void FuzzerPassDonateModules::HandleTypeOrValue( + const opt::Instruction& type_or_value, + std::map* original_id_to_donated_id) { + // The type/value instruction generates a result id, and we need to associate + // the donor's result id with a new result id. That new result id will either + // be the id of some existing instruction, or a fresh id. This variable + // captures it. + uint32_t new_result_id; - } break; - case SpvOpTypeArray: { - // It is OK to have multiple structurally identical array types, so - // we go ahead and add a remapped version of the type declared by the - // donor. - uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddTypeArray( - new_result_id, original_id_to_donated_id->at(component_type_id), - original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(1)))); - } break; - case SpvOpTypeRuntimeArray: { - // A runtime array is allowed as the final member of an SSBO. During - // donation we turn runtime arrays into fixed-size arrays. For dead - // code donations this is OK because the array is never indexed into at - // runtime, so it does not matter what its size is. For live-safe code, - // all accesses are made in-bounds, so this is also OK. - // - // The special OpArrayLength instruction, which works on runtime arrays, - // is rewritten to yield the fixed length that is used for the array. - - uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + // Decide how to handle each kind of instruction on a case-by-case basis. + // + // Because the donor module is required to be valid, when we encounter a + // type comprised of component types (e.g. an aggregate or pointer), we know + // that its component types will have been considered previously, and that + // |original_id_to_donated_id| will already contain an entry for them. + switch (type_or_value.opcode()) { + case SpvOpTypeImage: + case SpvOpTypeSampledImage: + case SpvOpTypeSampler: + // We do not donate types and variables that relate to images and + // samplers, so we skip these types and subsequently skip anything that + // depends on them. + return; + case SpvOpTypeVoid: { + // Void has to exist already in order for us to have an entry point. + // Get the existing id of void. + opt::analysis::Void void_type; + new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type); + assert(new_result_id && + "The module being transformed will always have 'void' type " + "declared."); + } break; + case SpvOpTypeBool: { + // Bool cannot be declared multiple times, so use its existing id if + // present, or add a declaration of Bool with a fresh id if not. + opt::analysis::Bool bool_type; + auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type); + if (bool_type_id) { + new_result_id = bool_type_id; + } else { new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddTypeArray( - new_result_id, original_id_to_donated_id->at(component_type_id), - FindOrCreate32BitIntegerConstant( - GetFuzzerContext()->GetRandomSizeForNewArray(), false))); - } break; - case SpvOpTypeStruct: { - // Similar to SpvOpTypeArray. - std::vector member_type_ids; - for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { - auto component_type_id = type_or_value.GetSingleWordInOperand(i); - member_type_ids.push_back( - original_id_to_donated_id->at(component_type_id)); - } + ApplyTransformation(TransformationAddTypeBoolean(new_result_id)); + } + } break; + case SpvOpTypeInt: { + // Int cannot be declared multiple times with the same width and + // signedness, so check whether an existing identical Int type is + // present and use its id if so. Otherwise add a declaration of the + // Int type used by the donor, with a fresh id. + const uint32_t width = type_or_value.GetSingleWordInOperand(0); + const bool is_signed = + static_cast(type_or_value.GetSingleWordInOperand(1)); + opt::analysis::Integer int_type(width, is_signed); + auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type); + if (int_type_id) { + new_result_id = int_type_id; + } else { new_result_id = GetFuzzerContext()->GetFreshId(); ApplyTransformation( - TransformationAddTypeStruct(new_result_id, member_type_ids)); - } break; - case SpvOpTypePointer: { - // Similar to SpvOpTypeArray. - uint32_t pointee_type_id = type_or_value.GetSingleWordInOperand(1); - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddTypePointer( - new_result_id, - AdaptStorageClass(static_cast( - type_or_value.GetSingleWordInOperand(0))), - original_id_to_donated_id->at(pointee_type_id))); - } break; - case SpvOpTypeFunction: { - // It is not OK to have multiple function types that use identical ids - // for their return and parameter types. We thus go through all - // existing function types to look for a match. We do not use the - // type manager here because we want to regard two function types that - // are structurally identical but that differ with respect to the - // actual ids used for pointer types as different. - // - // Example: - // - // %1 = OpTypeVoid - // %2 = OpTypeInt 32 0 - // %3 = OpTypePointer Function %2 - // %4 = OpTypePointer Function %2 - // %5 = OpTypeFunction %1 %3 - // %6 = OpTypeFunction %1 %4 - // - // We regard %5 and %6 as distinct function types here, even though - // they both have the form "uint32* -> void" - - std::vector return_and_parameter_types; - for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { - uint32_t return_or_parameter_type = - type_or_value.GetSingleWordInOperand(i); - return_and_parameter_types.push_back( - original_id_to_donated_id->at(return_or_parameter_type)); - } - uint32_t existing_function_id = fuzzerutil::FindFunctionType( - GetIRContext(), return_and_parameter_types); - if (existing_function_id) { - new_result_id = existing_function_id; - } else { - // No match was found, so add a remapped version of the function type - // to the module, with a fresh id. - new_result_id = GetFuzzerContext()->GetFreshId(); - std::vector argument_type_ids; - for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) { - argument_type_ids.push_back(original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(i))); - } - ApplyTransformation(TransformationAddTypeFunction( - new_result_id, - original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(0)), - argument_type_ids)); - } - } break; - case SpvOpConstantTrue: - case SpvOpConstantFalse: { - // It is OK to have duplicate definitions of True and False, so add - // these to the module, using a remapped Bool type. - new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddConstantBoolean( - new_result_id, type_or_value.opcode() == SpvOpConstantTrue)); - } break; - case SpvOpConstant: { - // It is OK to have duplicate constant definitions, so add this to the - // module using a remapped result type. - new_result_id = GetFuzzerContext()->GetFreshId(); - std::vector data_words; - type_or_value.ForEachInOperand( - [&data_words](const uint32_t* in_operand) { - data_words.push_back(*in_operand); - }); - ApplyTransformation(TransformationAddConstantScalar( - new_result_id, - original_id_to_donated_id->at(type_or_value.type_id()), - data_words)); - } break; - case SpvOpConstantComposite: { - assert(original_id_to_donated_id->count(type_or_value.type_id()) && - "Composite types for which it is possible to create a constant " - "should have been donated."); - - // It is OK to have duplicate constant composite definitions, so add - // this to the module using remapped versions of all consituent ids and - // the result type. + TransformationAddTypeInt(new_result_id, width, is_signed)); + } + } break; + case SpvOpTypeFloat: { + // Similar to SpvOpTypeInt. + const uint32_t width = type_or_value.GetSingleWordInOperand(0); + opt::analysis::Float float_type(width); + auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type); + if (float_type_id) { + new_result_id = float_type_id; + } else { new_result_id = GetFuzzerContext()->GetFreshId(); - std::vector constituent_ids; - type_or_value.ForEachInId( - [&constituent_ids, - &original_id_to_donated_id](const uint32_t* constituent_id) { - assert(original_id_to_donated_id->count(*constituent_id) && - "The constants used to construct this composite should " - "have been donated."); - constituent_ids.push_back( - original_id_to_donated_id->at(*constituent_id)); - }); - ApplyTransformation(TransformationAddConstantComposite( - new_result_id, - original_id_to_donated_id->at(type_or_value.type_id()), - constituent_ids)); - } break; - case SpvOpConstantNull: { - // It is fine to have multiple OpConstantNull instructions of the same - // type, so we just add this to the recipient module. + ApplyTransformation(TransformationAddTypeFloat(new_result_id, width)); + } + } break; + case SpvOpTypeVector: { + // It is not legal to have two Vector type declarations with identical + // element types and element counts, so check whether an existing + // identical Vector type is present and use its id if so. Otherwise add + // a declaration of the Vector type used by the donor, with a fresh id. + + // When considering the vector's component type id, we look up the id + // use in the donor to find the id to which this has been remapped. + uint32_t component_type_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)); + auto component_type = + GetIRContext()->get_type_mgr()->GetType(component_type_id); + assert(component_type && "The base type should be registered."); + auto component_count = type_or_value.GetSingleWordInOperand(1); + opt::analysis::Vector vector_type(component_type, component_count); + auto vector_type_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); + if (vector_type_id) { + new_result_id = vector_type_id; + } else { new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddConstantNull( - new_result_id, - original_id_to_donated_id->at(type_or_value.type_id()))); - } break; - case SpvOpVariable: { - // This is a global variable that could have one of various storage - // classes. However, we change all global variable pointer storage - // classes (such as Uniform, Input and Output) to private when donating - // pointer types, with the exception of the Workgroup storage class. - // - // Thus this variable's pointer type is guaranteed to have storage class - // Private or Workgroup. - // - // We add a global variable with either Private or Workgroup storage - // class, using remapped versions of the result type and initializer ids - // for the global variable in the donor. - // - // We regard the added variable as having an irrelevant value. This - // means that future passes can add stores to the variable in any - // way they wish, and pass them as pointer parameters to functions - // without worrying about whether their data might get modified. + ApplyTransformation(TransformationAddTypeVector( + new_result_id, component_type_id, component_count)); + } + } break; + case SpvOpTypeMatrix: { + // Similar to SpvOpTypeVector. + uint32_t column_type_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)); + auto column_type = + GetIRContext()->get_type_mgr()->GetType(column_type_id); + assert(column_type && column_type->AsVector() && + "The column type should be a registered vector type."); + auto column_count = type_or_value.GetSingleWordInOperand(1); + opt::analysis::Matrix matrix_type(column_type, column_count); + auto matrix_type_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type); + if (matrix_type_id) { + new_result_id = matrix_type_id; + } else { new_result_id = GetFuzzerContext()->GetFreshId(); - uint32_t remapped_pointer_type = - original_id_to_donated_id->at(type_or_value.type_id()); - uint32_t initializer_id; - SpvStorageClass storage_class = - static_cast(type_or_value.GetSingleWordInOperand( - 0)) == SpvStorageClassWorkgroup - ? SpvStorageClassWorkgroup - : SpvStorageClassPrivate; - if (type_or_value.NumInOperands() == 1) { - // The variable did not have an initializer. Initialize it to zero - // if it has Private storage class (to limit problems associated with - // uninitialized data), and leave it uninitialized if it has Workgroup - // storage class (as Workgroup variables cannot have initializers). - - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3275): we - // could initialize Workgroup variables at the start of an entry - // point, and should do so if their uninitialized nature proves - // problematic. - initializer_id = - storage_class == SpvStorageClassWorkgroup - ? 0 - : FindOrCreateZeroConstant( - fuzzerutil::GetPointeeTypeIdFromPointerType( - GetIRContext(), remapped_pointer_type)); - } else { - // The variable already had an initializer; use its remapped id. - initializer_id = original_id_to_donated_id->at( - type_or_value.GetSingleWordInOperand(1)); + ApplyTransformation(TransformationAddTypeMatrix( + new_result_id, column_type_id, column_count)); + } + + } break; + case SpvOpTypeArray: { + // It is OK to have multiple structurally identical array types, so + // we go ahead and add a remapped version of the type declared by the + // donor. + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate the component type of this array type, so we + // cannot donate the array type. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeArray( + new_result_id, original_id_to_donated_id->at(component_type_id), + original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(1)))); + } break; + case SpvOpTypeRuntimeArray: { + // A runtime array is allowed as the final member of an SSBO. During + // donation we turn runtime arrays into fixed-size arrays. For dead + // code donations this is OK because the array is never indexed into at + // runtime, so it does not matter what its size is. For live-safe code, + // all accesses are made in-bounds, so this is also OK. + // + // The special OpArrayLength instruction, which works on runtime arrays, + // is rewritten to yield the fixed length that is used for the array. + + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate the component type of this runtime array type, so + // we cannot donate it as a fixed-size array. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeArray( + new_result_id, original_id_to_donated_id->at(component_type_id), + FindOrCreate32BitIntegerConstant( + GetFuzzerContext()->GetRandomSizeForNewArray(), false))); + } break; + case SpvOpTypeStruct: { + // Similar to SpvOpTypeArray. + std::vector member_type_ids; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + auto component_type_id = type_or_value.GetSingleWordInOperand(i); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate every member type for this struct type, so we + // cannot donate the struct type. + return; } - ApplyTransformation(TransformationAddGlobalVariable( - new_result_id, remapped_pointer_type, storage_class, initializer_id, - true)); - } break; - case SpvOpUndef: { - // It is fine to have multiple Undef instructions of the same type, so - // we just add this to the recipient module. + member_type_ids.push_back( + original_id_to_donated_id->at(component_type_id)); + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeStruct(new_result_id, member_type_ids)); + } break; + case SpvOpTypePointer: { + // Similar to SpvOpTypeArray. + uint32_t pointee_type_id = type_or_value.GetSingleWordInOperand(1); + if (!original_id_to_donated_id->count(pointee_type_id)) { + // We did not donate the pointee type for this pointer type, so we + // cannot donate the pointer type. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypePointer( + new_result_id, + AdaptStorageClass(static_cast( + type_or_value.GetSingleWordInOperand(0))), + original_id_to_donated_id->at(pointee_type_id))); + } break; + case SpvOpTypeFunction: { + // It is not OK to have multiple function types that use identical ids + // for their return and parameter types. We thus go through all + // existing function types to look for a match. We do not use the + // type manager here because we want to regard two function types that + // are structurally identical but that differ with respect to the + // actual ids used for pointer types as different. + // + // Example: + // + // %1 = OpTypeVoid + // %2 = OpTypeInt 32 0 + // %3 = OpTypePointer Function %2 + // %4 = OpTypePointer Function %2 + // %5 = OpTypeFunction %1 %3 + // %6 = OpTypeFunction %1 %4 + // + // We regard %5 and %6 as distinct function types here, even though + // they both have the form "uint32* -> void" + + std::vector return_and_parameter_types; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + uint32_t return_or_parameter_type = + type_or_value.GetSingleWordInOperand(i); + if (!original_id_to_donated_id->count(return_or_parameter_type)) { + // We did not donate every return/parameter type for this function + // type, so we cannot donate the function type. + return; + } + return_and_parameter_types.push_back( + original_id_to_donated_id->at(return_or_parameter_type)); + } + uint32_t existing_function_id = fuzzerutil::FindFunctionType( + GetIRContext(), return_and_parameter_types); + if (existing_function_id) { + new_result_id = existing_function_id; + } else { + // No match was found, so add a remapped version of the function type + // to the module, with a fresh id. new_result_id = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddGlobalUndef( + std::vector argument_type_ids; + for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) { + argument_type_ids.push_back(original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(i))); + } + ApplyTransformation(TransformationAddTypeFunction( new_result_id, - original_id_to_donated_id->at(type_or_value.type_id()))); - } break; - default: { - assert(0 && "Unknown type/value."); - new_result_id = 0; - } break; - } - // Update the id mapping to associate the instruction's result id with its - // corresponding id in the recipient. - original_id_to_donated_id->insert( - {type_or_value.result_id(), new_result_id}); + original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)), + argument_type_ids)); + } + } break; + case SpvOpConstantTrue: + case SpvOpConstantFalse: { + // It is OK to have duplicate definitions of True and False, so add + // these to the module, using a remapped Bool type. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantBoolean( + new_result_id, type_or_value.opcode() == SpvOpConstantTrue)); + } break; + case SpvOpConstant: { + // It is OK to have duplicate constant definitions, so add this to the + // module using a remapped result type. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector data_words; + type_or_value.ForEachInOperand([&data_words](const uint32_t* in_operand) { + data_words.push_back(*in_operand); + }); + ApplyTransformation(TransformationAddConstantScalar( + new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), + data_words)); + } break; + case SpvOpConstantComposite: { + assert(original_id_to_donated_id->count(type_or_value.type_id()) && + "Composite types for which it is possible to create a constant " + "should have been donated."); + + // It is OK to have duplicate constant composite definitions, so add + // this to the module using remapped versions of all consituent ids and + // the result type. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector constituent_ids; + type_or_value.ForEachInId([&constituent_ids, &original_id_to_donated_id]( + const uint32_t* constituent_id) { + assert(original_id_to_donated_id->count(*constituent_id) && + "The constants used to construct this composite should " + "have been donated."); + constituent_ids.push_back( + original_id_to_donated_id->at(*constituent_id)); + }); + ApplyTransformation(TransformationAddConstantComposite( + new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), + constituent_ids)); + } break; + case SpvOpConstantNull: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the type associated with this null constant, so + // we cannot donate the null constant. + return; + } + + // It is fine to have multiple OpConstantNull instructions of the same + // type, so we just add this to the recipient module. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantNull( + new_result_id, + original_id_to_donated_id->at(type_or_value.type_id()))); + } break; + case SpvOpVariable: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the pointer type associated with this variable, + // so we cannot donate the variable. + return; + } + + // This is a global variable that could have one of various storage + // classes. However, we change all global variable pointer storage + // classes (such as Uniform, Input and Output) to private when donating + // pointer types, with the exception of the Workgroup storage class. + // + // Thus this variable's pointer type is guaranteed to have storage class + // Private or Workgroup. + // + // We add a global variable with either Private or Workgroup storage + // class, using remapped versions of the result type and initializer ids + // for the global variable in the donor. + // + // We regard the added variable as having an irrelevant value. This + // means that future passes can add stores to the variable in any + // way they wish, and pass them as pointer parameters to functions + // without worrying about whether their data might get modified. + new_result_id = GetFuzzerContext()->GetFreshId(); + uint32_t remapped_pointer_type = + original_id_to_donated_id->at(type_or_value.type_id()); + uint32_t initializer_id; + SpvStorageClass storage_class = + static_cast(type_or_value.GetSingleWordInOperand( + 0)) == SpvStorageClassWorkgroup + ? SpvStorageClassWorkgroup + : SpvStorageClassPrivate; + if (type_or_value.NumInOperands() == 1) { + // The variable did not have an initializer. Initialize it to zero + // if it has Private storage class (to limit problems associated with + // uninitialized data), and leave it uninitialized if it has Workgroup + // storage class (as Workgroup variables cannot have initializers). + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3275): we + // could initialize Workgroup variables at the start of an entry + // point, and should do so if their uninitialized nature proves + // problematic. + initializer_id = storage_class == SpvStorageClassWorkgroup + ? 0 + : FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), remapped_pointer_type)); + } else { + // The variable already had an initializer; use its remapped id. + initializer_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(1)); + } + ApplyTransformation( + TransformationAddGlobalVariable(new_result_id, remapped_pointer_type, + storage_class, initializer_id, true)); + } break; + case SpvOpUndef: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the type associated with this undef, so we cannot + // donate the undef. + return; + } + + // It is fine to have multiple Undef instructions of the same type, so + // we just add this to the recipient module. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddGlobalUndef( + new_result_id, + original_id_to_donated_id->at(type_or_value.type_id()))); + } break; + default: { + assert(0 && "Unknown type/value."); + new_result_id = 0; + } break; } + + // Update the id mapping to associate the instruction's result id with its + // corresponding id in the recipient. + original_id_to_donated_id->insert({type_or_value.result_id(), new_result_id}); } void FuzzerPassDonateModules::HandleFunctions( @@ -525,314 +574,49 @@ void FuzzerPassDonateModules::HandleFunctions( } assert(function_to_donate && "Function to be donated was not found."); + if (!original_id_to_donated_id->count( + function_to_donate->DefInst().GetSingleWordInOperand(1))) { + // We were not able to donate this function's type, so we cannot donate + // the function. + continue; + } + // We will collect up protobuf messages representing the donor function's // instructions here, and use them to create an AddFunction transformation. std::vector donated_instructions; - // Scan through the function, remapping each result id that it generates to - // a fresh id. This is necessary because functions include forward - // references, e.g. to labels. - function_to_donate->ForEachInst([this, donor_ir_context, - &original_id_to_donated_id]( - const opt::Instruction* instruction) { - if (!instruction->result_id()) { - return; - } - if (IgnoreInstruction(instruction)) { - if (instruction->opcode() == SpvOpArrayLength) { - // We treat the OpArrayLength instruction specially. In the donor - // shader this gets the length of a runtime array that is the final - // member of a struct. During donation, we will have converted the - // runtime array type, and the associated struct field, into a fixed- - // size array. We can then use the constant size of this fixed-sized - // array wherever we would have used the result of an OpArrayLength - // instruction. - uint32_t donated_variable_id = original_id_to_donated_id->at( - instruction->GetSingleWordInOperand(0)); - auto donated_variable_instruction = - GetIRContext()->get_def_use_mgr()->GetDef(donated_variable_id); - auto pointer_to_struct_instruction = - GetIRContext()->get_def_use_mgr()->GetDef( - donated_variable_instruction->type_id()); - assert(pointer_to_struct_instruction->opcode() == SpvOpTypePointer && - "Type of variable must be pointer."); - auto donated_struct_type_instruction = - GetIRContext()->get_def_use_mgr()->GetDef( - pointer_to_struct_instruction->GetSingleWordInOperand(1)); - assert( - donated_struct_type_instruction->opcode() == SpvOpTypeStruct && - "Pointee type of pointer used by OpArrayLength must be struct."); - assert(donated_struct_type_instruction->NumInOperands() == - instruction->GetSingleWordInOperand(1) + 1 && - "OpArrayLength must refer to the final member of the given " - "struct."); - uint32_t fixed_size_array_type_id = - donated_struct_type_instruction->GetSingleWordInOperand( - donated_struct_type_instruction->NumInOperands() - 1); - auto fixed_size_array_type_instruction = - GetIRContext()->get_def_use_mgr()->GetDef( - fixed_size_array_type_id); - assert(fixed_size_array_type_instruction->opcode() == - SpvOpTypeArray && - "The donated array type must be fixed-size."); - auto array_size_id = - fixed_size_array_type_instruction->GetSingleWordInOperand(1); - original_id_to_donated_id->insert( - {instruction->result_id(), array_size_id}); - } else if (instruction->type_id()) { - // If the ignored instruction has a basic result type then we - // associate its result id with a constant of that type, so that - // instructions that use the result id will use the constant instead. - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): - // Using this particular constant is arbitrary, so if we have a - // mechanism for noting that an id use is arbitrary and could be - // fuzzed we should use it here. - auto type_inst = donor_ir_context->get_def_use_mgr()->GetDef( - instruction->type_id()); - switch (type_inst->opcode()) { - case SpvOpTypeArray: - case SpvOpTypeBool: - case SpvOpTypeFloat: - case SpvOpTypeInt: - case SpvOpTypeStruct: - case SpvOpTypeVector: - case SpvOpTypeMatrix: - original_id_to_donated_id->insert( - {instruction->result_id(), - FindOrCreateZeroConstant( - original_id_to_donated_id->at(instruction->type_id()))}); - default: - break; - } - } - } else { - original_id_to_donated_id->insert( - {instruction->result_id(), GetFuzzerContext()->GetFreshId()}); - } - }); + // This set tracks the ids of those instructions for which donation was + // completely skipped: neither the instruction nor a substitute for it was + // donated. + std::set skipped_instructions; // Consider every instruction of the donor function. - function_to_donate->ForEachInst([this, &donated_instructions, - &original_id_to_donated_id]( - const opt::Instruction* instruction) { - if (IgnoreInstruction(instruction)) { - return; - } - - // Get the instruction's input operands into donation-ready form, - // remapping any id uses in the process. - opt::Instruction::OperandList input_operands; - - // Consider each input operand in turn. - for (uint32_t in_operand_index = 0; - in_operand_index < instruction->NumInOperands(); - in_operand_index++) { - std::vector operand_data; - const opt::Operand& in_operand = - instruction->GetInOperand(in_operand_index); - switch (in_operand.type) { - case SPV_OPERAND_TYPE_ID: - case SPV_OPERAND_TYPE_TYPE_ID: - case SPV_OPERAND_TYPE_RESULT_ID: - case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: - case SPV_OPERAND_TYPE_SCOPE_ID: - // This is an id operand - it consists of a single word of data, - // which needs to be remapped so that it is replaced with the - // donated form of the id. - operand_data.push_back( - original_id_to_donated_id->at(in_operand.words[0])); - break; - default: - // For non-id operands, we just add each of the data words. - for (auto word : in_operand.words) { - operand_data.push_back(word); - } - break; - } - input_operands.push_back({in_operand.type, operand_data}); - } - - if (instruction->opcode() == SpvOpVariable && - instruction->NumInOperands() == 1) { - // This is an uninitialized local variable. Initialize it to zero. - input_operands.push_back( - {SPV_OPERAND_TYPE_ID, - {FindOrCreateZeroConstant( - fuzzerutil::GetPointeeTypeIdFromPointerType( - GetIRContext(), - original_id_to_donated_id->at(instruction->type_id())))}}); - } - - // Remap the result type and result id (if present) of the - // instruction, and turn it into a protobuf message. - donated_instructions.push_back(MakeInstructionMessage( - instruction->opcode(), - instruction->type_id() - ? original_id_to_donated_id->at(instruction->type_id()) - : 0, - instruction->result_id() - ? original_id_to_donated_id->at(instruction->result_id()) - : 0, - input_operands)); - }); - - if (make_livesafe) { - // Various types and constants must be in place for a function to be made - // live-safe. Add them if not already present. - FindOrCreateBoolType(); // Needed for comparisons - FindOrCreatePointerTo32BitIntegerType( - false, SpvStorageClassFunction); // Needed for adding loop limiters - FindOrCreate32BitIntegerConstant( - 0, false); // Needed for initializing loop limiters - FindOrCreate32BitIntegerConstant( - 1, false); // Needed for incrementing loop limiters - - // Get a fresh id for the variable that will be used as a loop limiter. - const uint32_t loop_limiter_variable_id = - GetFuzzerContext()->GetFreshId(); - // Choose a random loop limit, and add the required constant to the - // module if not already there. - const uint32_t loop_limit = FindOrCreate32BitIntegerConstant( - GetFuzzerContext()->GetRandomLoopLimit(), false); - - // Consider every loop header in the function to donate, and create a - // structure capturing the ids to be used for manipulating the loop - // limiter each time the loop is iterated. - std::vector loop_limiters; - for (auto& block : *function_to_donate) { - if (block.IsLoopHeader()) { - protobufs::LoopLimiterInfo loop_limiter; - // Grab the loop header's id, mapped to its donated value. - loop_limiter.set_loop_header_id( - original_id_to_donated_id->at(block.id())); - // Get fresh ids that will be used to load the loop limiter, increment - // it, compare it with the loop limit, and an id for a new block that - // will contain the loop's original terminator. - loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId()); - loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId()); - loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId()); - loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId()); - loop_limiters.emplace_back(loop_limiter); - } - } - - // Consider every access chain in the function to donate, and create a - // structure containing the ids necessary to clamp the access chain - // indices to be in-bounds. - std::vector - access_chain_clamping_info; - for (auto& block : *function_to_donate) { - for (auto& inst : block) { - switch (inst.opcode()) { - case SpvOpAccessChain: - case SpvOpInBoundsAccessChain: { - protobufs::AccessChainClampingInfo clamping_info; - clamping_info.set_access_chain_id( - original_id_to_donated_id->at(inst.result_id())); - - auto base_object = donor_ir_context->get_def_use_mgr()->GetDef( - inst.GetSingleWordInOperand(0)); - assert(base_object && "The base object must exist."); - auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef( - base_object->type_id()); - assert(pointer_type && - pointer_type->opcode() == SpvOpTypePointer && - "The base object must have pointer type."); - - auto should_be_composite_type = - donor_ir_context->get_def_use_mgr()->GetDef( - pointer_type->GetSingleWordInOperand(1)); - - // Walk the access chain, creating fresh ids to facilitate - // clamping each index. For simplicity we do this for every - // index, even though constant indices will not end up being - // clamped. - for (uint32_t index = 1; index < inst.NumInOperands(); index++) { - auto compare_and_select_ids = - clamping_info.add_compare_and_select_ids(); - compare_and_select_ids->set_first( - GetFuzzerContext()->GetFreshId()); - compare_and_select_ids->set_second( - GetFuzzerContext()->GetFreshId()); - - // Get the bound for the component being indexed into. - uint32_t bound; - if (should_be_composite_type->opcode() == - SpvOpTypeRuntimeArray) { - // The donor is indexing into a runtime array. We do not - // donate runtime arrays. Instead, we donate a corresponding - // fixed-size array for every runtime array. We should thus - // find that donor composite type's result id maps to a fixed- - // size array. - auto fixed_size_array_type = - GetIRContext()->get_def_use_mgr()->GetDef( - original_id_to_donated_id->at( - should_be_composite_type->result_id())); - assert(fixed_size_array_type->opcode() == SpvOpTypeArray && - "A runtime array type in the donor should have been " - "replaced by a fixed-sized array in the recipient."); - // The size of this fixed-size array is a suitable bound. - bound = TransformationAddFunction::GetBoundForCompositeIndex( - GetIRContext(), *fixed_size_array_type); - } else { - bound = TransformationAddFunction::GetBoundForCompositeIndex( - donor_ir_context, *should_be_composite_type); - } - const uint32_t index_id = inst.GetSingleWordInOperand(index); - auto index_inst = - donor_ir_context->get_def_use_mgr()->GetDef(index_id); - auto index_type_inst = - donor_ir_context->get_def_use_mgr()->GetDef( - index_inst->type_id()); - assert(index_type_inst->opcode() == SpvOpTypeInt); - assert(index_type_inst->GetSingleWordInOperand(0) == 32); - opt::analysis::Integer* index_int_type = - donor_ir_context->get_type_mgr() - ->GetType(index_type_inst->result_id()) - ->AsInteger(); - if (index_inst->opcode() != SpvOpConstant) { - // We will have to clamp this index, so we need a constant - // whose value is one less than the bound, to compare - // against and to use as the clamped value. - FindOrCreate32BitIntegerConstant(bound - 1, - index_int_type->IsSigned()); - } - should_be_composite_type = - TransformationAddFunction::FollowCompositeIndex( - donor_ir_context, *should_be_composite_type, index_id); - } - access_chain_clamping_info.push_back(clamping_info); - break; - } - default: - break; + function_to_donate->ForEachInst( + [this, &donated_instructions, donor_ir_context, + &original_id_to_donated_id, + &skipped_instructions](const opt::Instruction* instruction) { + if (instruction->opcode() == SpvOpArrayLength) { + // We treat OpArrayLength specially. + HandleOpArrayLength(*instruction, original_id_to_donated_id, + &donated_instructions); + } else if (!CanDonateInstruction(donor_ir_context, *instruction, + *original_id_to_donated_id, + skipped_instructions)) { + // This is an instruction that we cannot directly donate. + HandleDifficultInstruction(*instruction, original_id_to_donated_id, + &donated_instructions, + &skipped_instructions); + } else { + PrepareInstructionForDonation(*instruction, donor_ir_context, + original_id_to_donated_id, + &donated_instructions); } - } - } + }); - // If the function contains OpKill or OpUnreachable instructions, and has - // non-void return type, then we need a value %v to use in order to turn - // these into instructions of the form OpReturn %v. - uint32_t kill_unreachable_return_value_id; - auto function_return_type_inst = - donor_ir_context->get_def_use_mgr()->GetDef( - function_to_donate->type_id()); - if (function_return_type_inst->opcode() == SpvOpTypeVoid) { - // The return type is void, so we don't need a return value. - kill_unreachable_return_value_id = 0; - } else { - // We do need a return value; we use zero. - assert(function_return_type_inst->opcode() != SpvOpTypePointer && - "Function return type must not be a pointer."); - kill_unreachable_return_value_id = - FindOrCreateZeroConstant(original_id_to_donated_id->at( - function_return_type_inst->result_id())); - } - // Add the function in a livesafe manner. - ApplyTransformation(TransformationAddFunction( - donated_instructions, loop_limiter_variable_id, loop_limit, - loop_limiters, kill_unreachable_return_value_id, - access_chain_clamping_info)); + if (make_livesafe) { + // Make the function livesafe and then add it. + AddLivesafeFunction(*function_to_donate, donor_ir_context, + *original_id_to_donated_id, donated_instructions); } else { // Add the function in a non-livesafe manner. ApplyTransformation(TransformationAddFunction(donated_instructions)); @@ -840,12 +624,20 @@ void FuzzerPassDonateModules::HandleFunctions( } } -bool FuzzerPassDonateModules::IgnoreInstruction( - const opt::Instruction* instruction) { - switch (instruction->opcode()) { - case SpvOpArrayLength: - // We ignore instructions that get the length of runtime arrays, because - // we turn all runtime arrays into fixed-size arrays. +bool FuzzerPassDonateModules::CanDonateInstruction( + opt::IRContext* donor_ir_context, const opt::Instruction& instruction, + const std::map& original_id_to_donated_id, + const std::set& skipped_instructions) const { + if (instruction.type_id() && + !original_id_to_donated_id.count(instruction.type_id())) { + // We could not donate the result type of this instruction, so we cannot + // donate the instruction. + return false; + } + + // Now consider instructions we specifically want to skip because we do not + // yet support them. + switch (instruction.opcode()) { case SpvOpAtomicLoad: case SpvOpAtomicStore: case SpvOpAtomicExchange: @@ -865,6 +657,93 @@ bool FuzzerPassDonateModules::IgnoreInstruction( // We conservatively ignore all atomic instructions at present. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3276): Consider // being less conservative here. + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageFetch: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImageRead: + case SpvOpImageWrite: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOpImageSparseFetch: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + case SpvOpImageSparseRead: + case SpvOpImageSampleFootprintNV: + case SpvOpImage: + case SpvOpImageQueryFormat: + case SpvOpImageQueryLevels: + case SpvOpImageQueryLod: + case SpvOpImageQueryOrder: + case SpvOpImageQuerySamples: + case SpvOpImageQuerySize: + case SpvOpImageQuerySizeLod: + case SpvOpSampledImage: + // We ignore all instructions related to accessing images, since we do not + // donate images. + return false; + case SpvOpLoad: + switch (donor_ir_context->get_def_use_mgr() + ->GetDef(instruction.type_id()) + ->opcode()) { + case SpvOpTypeImage: + case SpvOpTypeSampledImage: + case SpvOpTypeSampler: + // Again, we ignore instructions that relate to accessing images. + return false; + default: + break; + } + default: + break; + } + + // Examine each id input operand to the instruction. If it turns out that we + // have skipped any of these operands then we cannot donate the instruction. + bool result = true; + instruction.WhileEachInId( + [donor_ir_context, &original_id_to_donated_id, &result, + &skipped_instructions](const uint32_t* in_id) -> bool { + if (!original_id_to_donated_id.count(*in_id)) { + // We do not have a mapped result id for this id operand. That either + // means that it is a forward reference (which is OK), that we skipped + // the instruction that generated it (which is not OK), or that it is + // the id of a function that we did not donate (which is not OK). We + // check for the latter two cases. + if (skipped_instructions.count(*in_id) || + donor_ir_context->get_def_use_mgr()->GetDef(*in_id)->opcode() == + SpvOpFunction) { + result = false; + return false; + } + } + return true; + }); + return result; +} + +bool FuzzerPassDonateModules::IsBasicType( + const opt::Instruction& instruction) const { + switch (instruction.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeStruct: + case SpvOpTypeVector: return true; default: return false; @@ -917,5 +796,333 @@ FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder( return result; } +void FuzzerPassDonateModules::HandleOpArrayLength( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) const { + assert(instruction.opcode() == SpvOpArrayLength && + "Precondition: instruction must be OpArrayLength."); + uint32_t donated_variable_id = + original_id_to_donated_id->at(instruction.GetSingleWordInOperand(0)); + auto donated_variable_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(donated_variable_id); + auto pointer_to_struct_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + donated_variable_instruction->type_id()); + assert(pointer_to_struct_instruction->opcode() == SpvOpTypePointer && + "Type of variable must be pointer."); + auto donated_struct_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + pointer_to_struct_instruction->GetSingleWordInOperand(1)); + assert(donated_struct_type_instruction->opcode() == SpvOpTypeStruct && + "Pointee type of pointer used by OpArrayLength must be struct."); + assert(donated_struct_type_instruction->NumInOperands() == + instruction.GetSingleWordInOperand(1) + 1 && + "OpArrayLength must refer to the final member of the given " + "struct."); + uint32_t fixed_size_array_type_id = + donated_struct_type_instruction->GetSingleWordInOperand( + donated_struct_type_instruction->NumInOperands() - 1); + auto fixed_size_array_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(fixed_size_array_type_id); + assert(fixed_size_array_type_instruction->opcode() == SpvOpTypeArray && + "The donated array type must be fixed-size."); + auto array_size_id = + fixed_size_array_type_instruction->GetSingleWordInOperand(1); + + if (instruction.result_id() && + !original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + donated_instructions->push_back(MakeInstructionMessage( + SpvOpCopyObject, original_id_to_donated_id->at(instruction.type_id()), + original_id_to_donated_id->at(instruction.result_id()), + opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {array_size_id}}}))); +} + +void FuzzerPassDonateModules::HandleDifficultInstruction( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions, + std::set* skipped_instructions) { + if (!instruction.result_id()) { + // It does not generate a result id, so it can be ignored. + return; + } + if (!original_id_to_donated_id->count(instruction.type_id())) { + // We cannot handle this instruction's result type, so we need to skip it + // all together. + skipped_instructions->insert(instruction.result_id()); + return; + } + + // We now attempt to replace the instruction with an OpCopyObject. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3278): We could do + // something more refined here - we could check which operands to the + // instruction could not be donated and replace those operands with + // references to other ids (such as constants), so that we still get an + // instruction with the opcode and easy-to-handle operands of the donor + // instruction. + auto remapped_type_id = original_id_to_donated_id->at(instruction.type_id()); + if (!IsBasicType( + *GetIRContext()->get_def_use_mgr()->GetDef(remapped_type_id))) { + // The instruction has a non-basic result type, so we cannot replace it with + // an object copy of a constant. We thus skip it completely. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3279): We could + // instead look for an available id of the right type and generate an + // OpCopyObject of that id. + skipped_instructions->insert(instruction.result_id()); + return; + } + + // We are going to add an OpCopyObject instruction. Add a mapping for the + // result id of the original instruction if does not already exist (it may + // exist in the case that it has been forward-referenced). + if (!original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + // We find or add a zero constant to the receiving module for the type in + // question, and add an OpCopyObject instruction that copies this zero. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): + // Using this particular constant is arbitrary, so if we have a + // mechanism for noting that an id use is arbitrary and could be + // fuzzed we should use it here. + auto zero_constant = FindOrCreateZeroConstant(remapped_type_id); + donated_instructions->push_back(MakeInstructionMessage( + SpvOpCopyObject, remapped_type_id, + original_id_to_donated_id->at(instruction.result_id()), + opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {zero_constant}}}))); +} + +void FuzzerPassDonateModules::PrepareInstructionForDonation( + const opt::Instruction& instruction, opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) { + // Get the instruction's input operands into donation-ready form, + // remapping any id uses in the process. + opt::Instruction::OperandList input_operands; + + // Consider each input operand in turn. + for (uint32_t in_operand_index = 0; + in_operand_index < instruction.NumInOperands(); in_operand_index++) { + std::vector operand_data; + const opt::Operand& in_operand = instruction.GetInOperand(in_operand_index); + // Check whether this operand is an id. + if (spvIsIdType(in_operand.type)) { + // This is an id operand - it consists of a single word of data, + // which needs to be remapped so that it is replaced with the + // donated form of the id. + auto operand_id = in_operand.words[0]; + if (!original_id_to_donated_id->count(operand_id)) { + // This is a forward reference. We will choose a corresponding + // donor id for the referenced id and update the mapping to + // reflect it. + + // Keep release compilers happy because |donor_ir_context| is only used + // in this assertion. + (void)(donor_ir_context); + assert((donor_ir_context->get_def_use_mgr() + ->GetDef(operand_id) + ->opcode() == SpvOpLabel || + instruction.opcode() == SpvOpPhi) && + "Unsupported forward reference."); + original_id_to_donated_id->insert( + {operand_id, GetFuzzerContext()->GetFreshId()}); + } + operand_data.push_back(original_id_to_donated_id->at(operand_id)); + } else { + // For non-id operands, we just add each of the data words. + for (auto word : in_operand.words) { + operand_data.push_back(word); + } + } + input_operands.push_back({in_operand.type, operand_data}); + } + + if (instruction.opcode() == SpvOpVariable && + instruction.NumInOperands() == 1) { + // This is an uninitialized local variable. Initialize it to zero. + input_operands.push_back( + {SPV_OPERAND_TYPE_ID, + {FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), + original_id_to_donated_id->at(instruction.type_id())))}}); + } + + if (instruction.result_id() && + !original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + // Remap the result type and result id (if present) of the + // instruction, and turn it into a protobuf message. + donated_instructions->push_back(MakeInstructionMessage( + instruction.opcode(), + instruction.type_id() + ? original_id_to_donated_id->at(instruction.type_id()) + : 0, + instruction.result_id() + ? original_id_to_donated_id->at(instruction.result_id()) + : 0, + input_operands)); +} + +void FuzzerPassDonateModules::AddLivesafeFunction( + const opt::Function& function_to_donate, opt::IRContext* donor_ir_context, + const std::map& original_id_to_donated_id, + const std::vector& donated_instructions) { + // Various types and constants must be in place for a function to be made + // live-safe. Add them if not already present. + FindOrCreateBoolType(); // Needed for comparisons + FindOrCreatePointerTo32BitIntegerType( + false, SpvStorageClassFunction); // Needed for adding loop limiters + FindOrCreate32BitIntegerConstant( + 0, false); // Needed for initializing loop limiters + FindOrCreate32BitIntegerConstant( + 1, false); // Needed for incrementing loop limiters + + // Get a fresh id for the variable that will be used as a loop limiter. + const uint32_t loop_limiter_variable_id = GetFuzzerContext()->GetFreshId(); + // Choose a random loop limit, and add the required constant to the + // module if not already there. + const uint32_t loop_limit = FindOrCreate32BitIntegerConstant( + GetFuzzerContext()->GetRandomLoopLimit(), false); + + // Consider every loop header in the function to donate, and create a + // structure capturing the ids to be used for manipulating the loop + // limiter each time the loop is iterated. + std::vector loop_limiters; + for (auto& block : function_to_donate) { + if (block.IsLoopHeader()) { + protobufs::LoopLimiterInfo loop_limiter; + // Grab the loop header's id, mapped to its donated value. + loop_limiter.set_loop_header_id(original_id_to_donated_id.at(block.id())); + // Get fresh ids that will be used to load the loop limiter, increment + // it, compare it with the loop limit, and an id for a new block that + // will contain the loop's original terminator. + loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId()); + loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId()); + loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId()); + loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId()); + loop_limiters.emplace_back(loop_limiter); + } + } + + // Consider every access chain in the function to donate, and create a + // structure containing the ids necessary to clamp the access chain + // indices to be in-bounds. + std::vector access_chain_clamping_info; + for (auto& block : function_to_donate) { + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: { + protobufs::AccessChainClampingInfo clamping_info; + clamping_info.set_access_chain_id( + original_id_to_donated_id.at(inst.result_id())); + + auto base_object = donor_ir_context->get_def_use_mgr()->GetDef( + inst.GetSingleWordInOperand(0)); + assert(base_object && "The base object must exist."); + auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef( + base_object->type_id()); + assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer && + "The base object must have pointer type."); + + auto should_be_composite_type = + donor_ir_context->get_def_use_mgr()->GetDef( + pointer_type->GetSingleWordInOperand(1)); + + // Walk the access chain, creating fresh ids to facilitate + // clamping each index. For simplicity we do this for every + // index, even though constant indices will not end up being + // clamped. + for (uint32_t index = 1; index < inst.NumInOperands(); index++) { + auto compare_and_select_ids = + clamping_info.add_compare_and_select_ids(); + compare_and_select_ids->set_first(GetFuzzerContext()->GetFreshId()); + compare_and_select_ids->set_second( + GetFuzzerContext()->GetFreshId()); + + // Get the bound for the component being indexed into. + uint32_t bound; + if (should_be_composite_type->opcode() == SpvOpTypeRuntimeArray) { + // The donor is indexing into a runtime array. We do not + // donate runtime arrays. Instead, we donate a corresponding + // fixed-size array for every runtime array. We should thus + // find that donor composite type's result id maps to a fixed- + // size array. + auto fixed_size_array_type = + GetIRContext()->get_def_use_mgr()->GetDef( + original_id_to_donated_id.at( + should_be_composite_type->result_id())); + assert(fixed_size_array_type->opcode() == SpvOpTypeArray && + "A runtime array type in the donor should have been " + "replaced by a fixed-sized array in the recipient."); + // The size of this fixed-size array is a suitable bound. + bound = TransformationAddFunction::GetBoundForCompositeIndex( + GetIRContext(), *fixed_size_array_type); + } else { + bound = TransformationAddFunction::GetBoundForCompositeIndex( + donor_ir_context, *should_be_composite_type); + } + const uint32_t index_id = inst.GetSingleWordInOperand(index); + auto index_inst = + donor_ir_context->get_def_use_mgr()->GetDef(index_id); + auto index_type_inst = donor_ir_context->get_def_use_mgr()->GetDef( + index_inst->type_id()); + assert(index_type_inst->opcode() == SpvOpTypeInt); + assert(index_type_inst->GetSingleWordInOperand(0) == 32); + opt::analysis::Integer* index_int_type = + donor_ir_context->get_type_mgr() + ->GetType(index_type_inst->result_id()) + ->AsInteger(); + if (index_inst->opcode() != SpvOpConstant) { + // We will have to clamp this index, so we need a constant + // whose value is one less than the bound, to compare + // against and to use as the clamped value. + FindOrCreate32BitIntegerConstant(bound - 1, + index_int_type->IsSigned()); + } + should_be_composite_type = + TransformationAddFunction::FollowCompositeIndex( + donor_ir_context, *should_be_composite_type, index_id); + } + access_chain_clamping_info.push_back(clamping_info); + break; + } + default: + break; + } + } + } + + // If the function contains OpKill or OpUnreachable instructions, and has + // non-void return type, then we need a value %v to use in order to turn + // these into instructions of the form OpReturn %v. + uint32_t kill_unreachable_return_value_id; + auto function_return_type_inst = + donor_ir_context->get_def_use_mgr()->GetDef(function_to_donate.type_id()); + if (function_return_type_inst->opcode() == SpvOpTypeVoid) { + // The return type is void, so we don't need a return value. + kill_unreachable_return_value_id = 0; + } else { + // We do need a return value; we use zero. + assert(function_return_type_inst->opcode() != SpvOpTypePointer && + "Function return type must not be a pointer."); + kill_unreachable_return_value_id = FindOrCreateZeroConstant( + original_id_to_donated_id.at(function_return_type_inst->result_id())); + } + // Add the function in a livesafe manner. + ApplyTransformation(TransformationAddFunction( + donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters, + kill_unreachable_return_value_id, access_chain_clamping_info)); +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index 909f3bc4ed..e61572c53e 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -66,6 +66,11 @@ class FuzzerPassDonateModules : public FuzzerPass { opt::IRContext* donor_ir_context, std::map* original_id_to_donated_id); + // TODO comment + void HandleTypeOrValue( + const opt::Instruction& type_or_value, + std::map* original_id_to_donated_id); + // Assumes that |donor_ir_context| does not exhibit recursion. Considers the // functions in |donor_ir_context|'s call graph in a reverse-topologically- // sorted order (leaves-to-root), adding each function to the recipient @@ -80,8 +85,64 @@ class FuzzerPassDonateModules : public FuzzerPass { // During donation we will have to ignore some instructions, e.g. because they // use opcodes that we cannot support or because they reference the ids of // instructions that have not been donated. This function encapsulates the - // logic for deciding which instructions should be ignored. - bool IgnoreInstruction(const opt::Instruction* instruction); + // logic for deciding which whether instruction |instruction| from + // |donor_ir_context| can be donated. + bool CanDonateInstruction( + opt::IRContext* donor_ir_context, const opt::Instruction& instruction, + const std::map& original_id_to_donated_id, + const std::set& skipped_instructions) const; + + // We treat the OpArrayLength instruction specially. In the donor shader this + // instruction yields the length of a runtime array that is the final member + // of a struct. During donation, we will have converted the runtime array + // type, and the associated struct field, into a fixed-size array. + // + // Instead of donating this instruction, we turn it into an OpCopyObject + // instruction that copies the size of the fixed-size array. + void HandleOpArrayLength( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) const; + + // The instruction |instruction| is required to be an instruction that cannot + // be easily donated, either because it uses an unsupported opcode, has an + // unsupported result type, or uses id operands that could not be donated. + // + // If |instruction| generates a result id, the function attempts to add a + // substitute for |instruction| to |donated_instructions| that has the correct + // result type. If this cannot be done, the instruction's result id is added + // to |skipped_instructions|. The mapping from donor ids to recipient ids is + // managed by |original_id_to_donated_id|. + void HandleDifficultInstruction( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions, + std::set* skipped_instructions); + + // Adds an instruction based in |instruction| to |donated_instructions| in a + // form ready for donation. The original instruction comes from + // |donor_ir_context|, and |original_id_to_donated_id| maps ids from + // |donor_ir_context| to corresponding ids in the recipient module. + void PrepareInstructionForDonation( + const opt::Instruction& instruction, opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + std::vector* donated_instructions); + + // Requires that |donated_instructions| represents a prepared version of the + // instructions of |function_to_donate| (which comes from |donor_ir_context|) + // ready for donation, and |original_id_to_donated_id| maps ids from + // |donor_ir_context| to their corresponding ids in the recipient module. + // + // Adds a livesafe version of the function, based on |donated_instructions|, + // to the recipient module. + void AddLivesafeFunction( + const opt::Function& function_to_donate, opt::IRContext* donor_ir_context, + const std::map& original_id_to_donated_id, + const std::vector& donated_instructions); + + // Returns true if and only if |instruction| is a scalar, vector, matrix, + // array or struct; i.e. it is not an opaque type. + bool IsBasicType(const opt::Instruction& instruction) const; // Returns the ids of all functions in |context| in a topological order in // relation to the call graph of |context|, which is assumed to be recursion- diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index bbc92b92be..432ca4f8a5 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -496,7 +496,8 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { TransformationContext transformation_context(&fact_manager, validator_options); - FuzzerContext fuzzer_context(MakeUnique(0).get(), 100); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -577,6 +578,486 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { ASSERT_TRUE(IsValid(env, recipient_context.get())); } +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + OpName %4 "main" + OpName %10 "mySampler" + OpName %21 "myTexture" + OpName %33 "v" + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + OpDecorate %22 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 2 + %15 = OpTypeVector %12 2 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %20 = OpTypePointer UniformConstant %7 + %21 = OpVariable %20 UniformConstant + %23 = OpConstant %12 1 + %25 = OpConstant %17 1 + %27 = OpTypeBool + %31 = OpTypeVector %6 4 + %32 = OpTypePointer Function %31 + %35 = OpConstantComposite %15 %23 %23 + %36 = OpConstant %12 3 + %37 = OpConstant %12 4 + %38 = OpConstantComposite %15 %36 %37 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %32 Function + %11 = OpLoad %8 %10 + %14 = OpImage %7 %11 + %16 = OpImageQuerySizeLod %15 %14 %13 + %19 = OpCompositeExtract %12 %16 0 + %22 = OpLoad %7 %21 + %24 = OpImageQuerySizeLod %15 %22 %23 + %26 = OpCompositeExtract %12 %24 1 + %28 = OpSGreaterThan %27 %19 %26 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %41 + %29 = OpLabel + %34 = OpLoad %8 %10 + %39 = OpImage %7 %34 + %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38 + OpStore %33 %40 + OpBranch %30 + %41 = OpLabel + %42 = OpLoad %7 %21 + %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38 + OpStore %33 %43 + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 0 + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 64 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %23 = OpTypeFloat 32 + %6 = OpTypeImage %23 2D 2 0 0 1 Unknown + %47 = OpTypePointer UniformConstant %6 + %12 = OpVariable %47 UniformConstant + %15 = OpTypeSampler + %55 = OpTypePointer UniformConstant %15 + %17 = OpTypeSampledImage %6 + %16 = OpVariable %55 UniformConstant + %37 = OpTypeVector %23 4 + %109 = OpConstant %23 0 + %66 = OpConstantComposite %37 %109 %109 %109 %109 + %56 = OpTypeBool + %54 = OpConstantTrue %56 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %50 + %50 = OpLabel + %51 = OpPhi %37 %66 %5 %111 %53 + OpLoopMerge %52 %53 None + OpBranchConditional %54 %53 %52 + %53 = OpLabel + %106 = OpLoad %6 %12 + %107 = OpLoad %15 %16 + %110 = OpSampledImage %17 %106 %107 + %111 = OpImageSampleImplicitLod %37 %110 %66 Bias %109 + OpBranch %50 + %52 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + OpName %4 "main" + OpName %10 "mySampler" + OpName %21 "myTexture" + OpName %33 "v" + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + OpDecorate %22 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 2 + %15 = OpTypeVector %12 2 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %20 = OpTypePointer UniformConstant %7 + %21 = OpVariable %20 UniformConstant + %23 = OpConstant %12 1 + %25 = OpConstant %17 1 + %27 = OpTypeBool + %31 = OpTypeVector %6 4 + %32 = OpTypePointer Function %31 + %35 = OpConstantComposite %15 %23 %23 + %36 = OpConstant %12 3 + %37 = OpConstant %12 4 + %38 = OpConstantComposite %15 %36 %37 + %201 = OpTypeStruct %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %32 Function + %11 = OpLoad %8 %10 + %14 = OpImage %7 %11 + %22 = OpLoad %7 %21 + %200 = OpCompositeConstruct %201 %14 %22 + %202 = OpCompositeExtract %7 %200 0 + %203 = OpCompositeExtract %7 %200 1 + %24 = OpImageQuerySizeLod %15 %203 %23 + %16 = OpImageQuerySizeLod %15 %202 %13 + %26 = OpCompositeExtract %12 %24 1 + %19 = OpCompositeExtract %12 %16 0 + %28 = OpSGreaterThan %27 %19 %26 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %41 + %29 = OpLabel + %34 = OpLoad %8 %10 + %39 = OpImage %7 %34 + %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38 + OpStore %33 %40 + OpBranch %30 + %41 = OpLabel + %42 = OpLoad %7 %21 + %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38 + OpStore %33 %43 + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + OpName %4 "main" + OpName %10 "mySampler" + OpName %21 "myTexture" + OpName %33 "v" + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + OpDecorate %22 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 2 + %15 = OpTypeVector %12 2 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %20 = OpTypePointer UniformConstant %7 + %21 = OpVariable %20 UniformConstant + %23 = OpConstant %12 1 + %25 = OpConstant %17 1 + %27 = OpTypeBool + %31 = OpTypeVector %6 4 + %32 = OpTypePointer Function %31 + %35 = OpConstantComposite %15 %23 %23 + %36 = OpConstant %12 3 + %37 = OpConstant %12 4 + %38 = OpConstantComposite %15 %36 %37 + %201 = OpTypeFunction %15 %7 %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %32 Function + %11 = OpLoad %8 %10 + %14 = OpImage %7 %11 + %16 = OpFunctionCall %15 %200 %14 %13 + %19 = OpCompositeExtract %12 %16 0 + %22 = OpLoad %7 %21 + %24 = OpImageQuerySizeLod %15 %22 %23 + %26 = OpCompositeExtract %12 %24 1 + %28 = OpSGreaterThan %27 %19 %26 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %41 + %29 = OpLabel + %34 = OpLoad %8 %10 + %39 = OpImage %7 %34 + %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38 + OpStore %33 %40 + OpBranch %30 + %41 = OpLabel + %42 = OpLoad %7 %21 + %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38 + OpStore %33 %43 + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd + %200 = OpFunction %15 None %201 + %202 = OpFunctionParameter %7 + %203 = OpFunctionParameter %12 + %204 = OpLabel + %205 = OpImageQuerySizeLod %15 %202 %203 + OpReturnValue %205 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { std::string recipient_shader = R"( OpCapability Shader From 538512e8e8980cc01ab6501cd1cbd402d54f8491 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 7 Apr 2020 17:38:27 +0100 Subject: [PATCH 71/88] spirv-fuzz: Improve the handling of equation facts (#3281) The management of equation facts suffered from two problems: (1) The processing of an equation fact required the data descriptors used in the equation to be in canonical form. However, during fact processing it can be deduced that certain data descriptors are equivalent, causing their equivalence classes to be merged, and that could cause previously canonical data descriptors to no longer be canonical. (2) Related to this, if id equations were known about a canonical data descriptor dd1, and other id equations known about a different canonical data descriptor dd2, the equation facts about these data descriptors were not being merged in the event that dd1 and dd2 were deduced to be equivalent. This changes solves (1) by not requiring equation facts to be in canonical form while processing them, but instead always checking whether (not necessary canonical) data descriptors are equivalent when looking for corollaries of equation facts, rather than comparing them using ==. Problem (2) is solved by adding logic to merge sets of equations when data descriptors are made equivalent. In addition, the change also requires elements to be registered in an equivalence relation before they can be made equivalent, rather than being added (if not already present) at the point of being made equivalent. --- source/fuzz/equivalence_relation.h | 19 +- source/fuzz/fact_manager.cpp | 175 +++++++++++++----- test/fuzz/equivalence_relation_test.cpp | 9 + test/fuzz/fact_manager_test.cpp | 72 +++++++ ...ansformation_equation_instruction_test.cpp | 140 ++++++++++++++ 5 files changed, 362 insertions(+), 53 deletions(-) diff --git a/source/fuzz/equivalence_relation.h b/source/fuzz/equivalence_relation.h index 7bb8b66803..6d0b63eda3 100644 --- a/source/fuzz/equivalence_relation.h +++ b/source/fuzz/equivalence_relation.h @@ -68,17 +68,14 @@ namespace fuzz { template class EquivalenceRelation { public: - // Merges the equivalence classes associated with |value1| and |value2|. - // If any of these values was not previously in the equivalence relation, it - // is added to the pool of values known to be in the relation. + // Requires that |value1| and |value2| are already registered in the + // equivalence relation. Merges the equivalence classes associated with + // |value1| and |value2|. void MakeEquivalent(const T& value1, const T& value2) { - // Register each value if necessary. - for (auto value : {value1, value2}) { - if (!Exists(value)) { - // Register the value in the equivalence relation. - Register(value); - } - } + assert(Exists(value1) && + "Precondition: value1 must already be registered."); + assert(Exists(value2) && + "Precondition: value2 must already be registered."); // Look up canonical pointers to each of the values in the value pool. const T* value1_ptr = *value_set_.find(&value1); @@ -105,7 +102,7 @@ class EquivalenceRelation { // Requires that |value| is not known to the equivalence relation. Registers // it in its own equivalence class and returns a pointer to the equivalence // class representative. - const T* Register(T& value) { + const T* Register(const T& value) { assert(!Exists(value)); // This relies on T having a copy constructor. diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp index 31d3b947f8..790134117c 100644 --- a/source/fuzz/fact_manager.cpp +++ b/source/fuzz/fact_manager.cpp @@ -445,6 +445,15 @@ class FactManager::DataSynonymAndIdEquationFacts { // compute the closure only when a data synonym fact is *queried*. void ComputeClosureOfFacts(opt::IRContext* context) const; + // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets + // of equations that are known about them. + // + // This is a const method, despite the fact that it mutates the (mutable) + // set of facts about data descriptors because it is invoked in a lazy fashion + // when querying facts. + void MakeEquivalent(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) const; + // Returns true if and only if |dd1| and |dd2| are valid data descriptors // whose associated data have the same type (modulo integer signedness). bool DataDescriptorsAreWellFormedAndComparable( @@ -452,10 +461,11 @@ class FactManager::DataSynonymAndIdEquationFacts { const protobufs::DataDescriptor& dd2) const; // Requires that |lhs_dd| and every element of |rhs_dds| is present in the - // |synonymous_| equivalence relation and is its own representative. Records - // the fact that the equation "|lhs_dd| |opcode| |rhs_dds|" holds, and adds - // any corollaries, in the form of data synonym or equation facts, that - // follow from this and other known facts. + // |synonymous_| equivalence relation, but is not necessarily its own + // representative. Records the fact that the equation + // "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any + // corollaries, in the form of data synonym or equation facts, that follow + // from this and other known facts. void AddEquationFactRecursive( const protobufs::DataDescriptor& lhs_dd, SpvOp opcode, const std::vector& rhs_dds, @@ -493,7 +503,12 @@ class FactManager::DataSynonymAndIdEquationFacts { // All data descriptors occurring in equations are required to be present in // the |synonymous_| equivalence relation, and to be their own representatives // in that relation. - std::unordered_map< + // + // It is mutable because a closure computation can be triggered from a const + // method, and when a closure computation detects that two data descriptors + // are equivalent it is necessary to merge the equation facts for those data + // descriptors. + mutable std::unordered_map< const protobufs::DataDescriptor*, std::unordered_set> id_equations_; @@ -510,12 +525,10 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact( const protobufs::FactIdEquation& fact, opt::IRContext* context) { protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {}); - // Register the LHS in the equivalence relation if needed, and get a pointer - // to its representative. + // Register the LHS in the equivalence relation if needed. if (!synonymous_.Exists(lhs_dd)) { synonymous_.Register(lhs_dd); } - const protobufs::DataDescriptor* lhs_dd_ptr = synonymous_.Find(&lhs_dd); // Get equivalence class representatives for all ids used on the RHS of the // equation. @@ -529,10 +542,9 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact( } rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd)); } - // We now have the equation in a form where it refers exclusively to - // equivalence class representatives. Add it to our set of facts and work - // out any follow-on facts. - AddEquationFactRecursive(*lhs_dd_ptr, static_cast(fact.opcode()), + + // Now add the fact. + AddEquationFactRecursive(lhs_dd, static_cast(fact.opcode()), rhs_dd_ptrs, context); } @@ -540,27 +552,27 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( const protobufs::DataDescriptor& lhs_dd, SpvOp opcode, const std::vector& rhs_dds, opt::IRContext* context) { - // Precondition: all data descriptors referenced in this equation must be - // equivalence class representatives - i.e. the equation must be in canonical - // form. - assert(synonymous_.Exists(lhs_dd)); - assert(synonymous_.Find(&lhs_dd) == &lhs_dd); + assert(synonymous_.Exists(lhs_dd) && + "The LHS must be known to the equivalence relation."); for (auto rhs_dd : rhs_dds) { - (void)(rhs_dd); // Keep compilers happy in release mode. - assert(synonymous_.Exists(*rhs_dd)); - assert(synonymous_.Find(rhs_dd) == rhs_dd); + // Keep release compilers happy. + (void)(rhs_dd); + assert(synonymous_.Exists(*rhs_dd) && + "The RHS operands must be known to the equivalence relation."); } - if (id_equations_.count(&lhs_dd) == 0) { + auto lhs_dd_representative = synonymous_.Find(&lhs_dd); + + if (id_equations_.count(lhs_dd_representative) == 0) { // We have not seen an equation with this LHS before, so associate the LHS // with an initially empty set. id_equations_.insert( - {&lhs_dd, + {lhs_dd_representative, std::unordered_set()}); } { - auto existing_equations = id_equations_.find(&lhs_dd); + auto existing_equations = id_equations_.find(lhs_dd_representative); assert(existing_equations != id_equations_.end() && "A set of operations should be present, even if empty."); @@ -584,13 +596,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( for (auto equation : existing_first_operand_equations->second) { if (equation.opcode == SpvOpISub) { // Equation form: "a = (d - e) + c" - if (equation.operands[1] == rhs_dds[1]) { + if (synonymous_.IsEquivalent(*equation.operands[1], + *rhs_dds[1])) { // Equation form: "a = (d - c) + c" // We can thus infer "a = d" AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context); } - if (equation.operands[0] == rhs_dds[1]) { + if (synonymous_.IsEquivalent(*equation.operands[0], + *rhs_dds[1])) { // Equation form: "a = (c - e) + c" // We can thus infer "a = -e" AddEquationFactRecursive(lhs_dd, SpvOpSNegate, @@ -606,7 +620,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( for (auto equation : existing_second_operand_equations->second) { if (equation.opcode == SpvOpISub) { // Equation form: "a = b + (d - e)" - if (equation.operands[1] == rhs_dds[0]) { + if (synonymous_.IsEquivalent(*equation.operands[1], + *rhs_dds[0])) { // Equation form: "a = b + (d - b)" // We can thus infer "a = d" AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], @@ -626,13 +641,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( for (auto equation : existing_first_operand_equations->second) { if (equation.opcode == SpvOpIAdd) { // Equation form: "a = (d + e) - c" - if (equation.operands[0] == rhs_dds[1]) { + if (synonymous_.IsEquivalent(*equation.operands[0], + *rhs_dds[1])) { // Equation form: "a = (c + e) - c" // We can thus infer "a = e" AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context); } - if (equation.operands[1] == rhs_dds[1]) { + if (synonymous_.IsEquivalent(*equation.operands[1], + *rhs_dds[1])) { // Equation form: "a = (d + c) - c" // We can thus infer "a = d" AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], @@ -642,7 +659,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( if (equation.opcode == SpvOpISub) { // Equation form: "a = (d - e) - c" - if (equation.operands[0] == rhs_dds[1]) { + if (synonymous_.IsEquivalent(*equation.operands[0], + *rhs_dds[1])) { // Equation form: "a = (c - e) - c" // We can thus infer "a = -e" AddEquationFactRecursive(lhs_dd, SpvOpSNegate, @@ -659,13 +677,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( for (auto equation : existing_second_operand_equations->second) { if (equation.opcode == SpvOpIAdd) { // Equation form: "a = b - (d + e)" - if (equation.operands[0] == rhs_dds[0]) { + if (synonymous_.IsEquivalent(*equation.operands[0], + *rhs_dds[0])) { // Equation form: "a = b - (b + e)" // We can thus infer "a = -e" AddEquationFactRecursive(lhs_dd, SpvOpSNegate, {equation.operands[1]}, context); } - if (equation.operands[1] == rhs_dds[0]) { + if (synonymous_.IsEquivalent(*equation.operands[1], + *rhs_dds[0])) { // Equation form: "a = b - (d + b)" // We can thus infer "a = -d" AddEquationFactRecursive(lhs_dd, SpvOpSNegate, @@ -674,7 +694,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( } if (equation.opcode == SpvOpISub) { // Equation form: "a = b - (d - e)" - if (equation.operands[0] == rhs_dds[0]) { + if (synonymous_.IsEquivalent(*equation.operands[0], + *rhs_dds[0])) { // Equation form: "a = b - (b - e)" // We can thus infer "a = e" AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], @@ -712,12 +733,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive( assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2)); // Record that the data descriptors provided in the fact are equivalent. - synonymous_.MakeEquivalent(dd1, dd2); - // As we have updated the equivalence relation, we might be able to deduce - // more facts by performing a closure computation, so we record that such a - // computation is required; it will be performed next time a method answering - // a data synonym fact-related question is invoked. - closure_computation_required_ = true; + MakeEquivalent(dd1, dd2); // We now check whether this is a synonym about composite objects. If it is, // we can recursively add synonym facts about their associated sub-components. @@ -1029,10 +1045,7 @@ void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts( // synonymous. assert(DataDescriptorsAreWellFormedAndComparable( context, dd1_prefix, dd2_prefix)); - synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix); - // As we have added a new synonym fact, we might benefit from doing - // another pass over the equivalence relation. - closure_computation_required_ = true; + MakeEquivalent(dd1_prefix, dd2_prefix); // Now that we know this pair of data descriptors are synonymous, // there is no point recording how close they are to being // synonymous. @@ -1044,6 +1057,84 @@ void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts( } } +void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) const { + // Register the data descriptors if they are not already known to the + // equivalence relation. + for (const auto& dd : {dd1, dd2}) { + if (!synonymous_.Exists(dd)) { + synonymous_.Register(dd); + } + } + + if (synonymous_.IsEquivalent(dd1, dd2)) { + // The data descriptors are already known to be equivalent, so there is + // nothing to do. + return; + } + + // We must make the data descriptors equivalent, and also make sure any + // equation facts known about their representatives are merged. + + // Record the original equivalence class representatives of the data + // descriptors. + auto dd1_original_representative = synonymous_.Find(&dd1); + auto dd2_original_representative = synonymous_.Find(&dd2); + + // Make the data descriptors equivalent. + synonymous_.MakeEquivalent(dd1, dd2); + // As we have updated the equivalence relation, we might be able to deduce + // more facts by performing a closure computation, so we record that such a + // computation is required. + closure_computation_required_ = true; + + // At this point, exactly one of |dd1_original_representative| and + // |dd2_original_representative| will be the representative of the combined + // equivalence class. We work out which one of them is still the class + // representative and which one is no longer the class representative. + + auto still_representative = synonymous_.Find(dd1_original_representative) == + dd1_original_representative + ? dd1_original_representative + : dd2_original_representative; + auto no_longer_representative = + still_representative == dd1_original_representative + ? dd2_original_representative + : dd1_original_representative; + + assert(no_longer_representative != still_representative && + "The current and former representatives cannot be the same."); + + // We now need to add all equations about |no_longer_representative| to the + // set of equations known about |still_representative|. + + // Get the equations associated with |no_longer_representative|. + auto no_longer_representative_id_equations = + id_equations_.find(no_longer_representative); + if (no_longer_representative_id_equations != id_equations_.end()) { + // There are some equations to transfer. There might not yet be any + // equations about |still_representative|; create an empty set of equations + // if this is the case. + if (!id_equations_.count(still_representative)) { + id_equations_.insert( + {still_representative, + std::unordered_set()}); + } + auto still_representative_id_equations = + id_equations_.find(still_representative); + assert(still_representative_id_equations != id_equations_.end() && + "At this point there must be a set of equations."); + // Add all the equations known about |no_longer_representative| to the set + // of equations known about |still_representative|. + still_representative_id_equations->second.insert( + no_longer_representative_id_equations->second.begin(), + no_longer_representative_id_equations->second.end()); + } + // Delete the no longer-relevant equations about |no_longer_representative|. + id_equations_.erase(no_longer_representative); +} + bool FactManager::DataSynonymAndIdEquationFacts:: DataDescriptorsAreWellFormedAndComparable( opt::IRContext* context, const protobufs::DataDescriptor& dd1, diff --git a/test/fuzz/equivalence_relation_test.cpp b/test/fuzz/equivalence_relation_test.cpp index 3f2ea58fe8..280aa3a43b 100644 --- a/test/fuzz/equivalence_relation_test.cpp +++ b/test/fuzz/equivalence_relation_test.cpp @@ -47,6 +47,10 @@ TEST(EquivalenceRelationTest, BasicTest) { EquivalenceRelation relation; ASSERT_TRUE(relation.GetAllKnownValues().empty()); + for (uint32_t element = 0; element < 100; element++) { + relation.Register(element); + } + for (uint32_t element = 2; element < 80; element += 2) { relation.MakeEquivalent(0, element); relation.MakeEquivalent(element - 1, element + 1); @@ -122,6 +126,11 @@ TEST(EquivalenceRelationTest, DeterministicEquivalenceClassOrder) { EquivalenceRelation relation1; EquivalenceRelation relation2; + for (uint32_t i = 0; i < 1000; ++i) { + relation1.Register(i); + relation2.Register(i); + } + for (uint32_t i = 0; i < 1000; ++i) { if (i >= 10) { relation1.MakeEquivalent(i, i - 10); diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp index 2c79f128b5..625eb72ee9 100644 --- a/test/fuzz/fact_manager_test.cpp +++ b/test/fuzz/fact_manager_test.cpp @@ -1373,6 +1373,78 @@ TEST(FactManagerTest, AddSubNegateFacts2) { MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get())); } +TEST(FactManagerTest, EquationAndEquivalenceFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpISub %6 %15 %16 + %114 = OpCopyObject %6 %14 + %17 = OpIAdd %6 %114 %16 ; ==> synonymous(%17, %15) + %18 = OpIAdd %6 %16 %114 ; ==> synonymous(%17, %18, %15) + %19 = OpISub %6 %114 %15 + %119 = OpCopyObject %6 %19 + %20 = OpSNegate %6 %119 ; ==> synonymous(%20, %16) + %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15) + %22 = OpISub %6 %14 %18 + %220 = OpCopyObject %6 %22 + %23 = OpSNegate %6 %220 ; ==> synonymous(%23, %16) + 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; + + fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(114, {}), + MakeDataDescriptor(14, {}), context.get()); + fact_manager.AddFactIdEquation(17, SpvOpIAdd, {114, 16}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get())); + + fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 114}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get())); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get())); + + fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(119, {}), + MakeDataDescriptor(19, {}), context.get()); + fact_manager.AddFactIdEquation(20, SpvOpSNegate, {119}, context.get()); + + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get())); + + fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get()); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get())); + + fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get()); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}), + MakeDataDescriptor(220, {}), context.get()); + fact_manager.AddFactIdEquation(23, SpvOpSNegate, {220}, context.get()); + ASSERT_TRUE(fact_manager.IsSynonymous( + MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp index be9024e0a0..3155b1891f 100644 --- a/test/fuzz/transformation_equation_instruction_test.cpp +++ b/test/fuzz/transformation_equation_instruction_test.cpp @@ -486,6 +486,146 @@ TEST(TransformationEquationInstructionTest, AddSubNegate2) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationEquationInstructionTest, Miscellaneous1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + 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; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + auto transformation1 = TransformationEquationInstruction( + 522, SpvOpISub, {113, 113}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation2 = TransformationEquationInstruction( + 570, SpvOpIAdd, {522, 113}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %522 = OpISub %6 %113 %113 + %570 = OpIAdd %6 %522 %113 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {}), context.get())); + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, Miscellaneous2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + 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; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + auto transformation1 = TransformationEquationInstruction( + 522, SpvOpISub, {113, 113}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + transformation1.Apply(context.get(), &transformation_context); + ASSERT_TRUE(IsValid(env, context.get())); + + auto transformation2 = TransformationEquationInstruction( + 570, SpvOpIAdd, {522, 113}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + transformation2.Apply(context.get(), &transformation_context); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %522 = OpISub %6 %113 %113 + %570 = OpIAdd %6 %522 %113 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {}), context.get())); + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools From d0490ef080c2dda29b682b7dcd6ecc86b509deed Mon Sep 17 00:00:00 2001 From: Malacath-92 Date: Thu, 9 Apr 2020 20:46:08 +0200 Subject: [PATCH 72/88] Fix pch macro to ignore clang-cl (#3283) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef9ad112f9..9d0eb8b544 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,7 +249,7 @@ endif() # Precompiled header macro. Parameters are source file list and filename for pch cpp file. macro(spvtools_pch SRCS PCHPREFIX) - if(MSVC AND CMAKE_GENERATOR MATCHES "^Visual Studio") + if(MSVC AND CMAKE_GENERATOR MATCHES "^Visual Studio" AND NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(PCH_NAME "$(IntDir)\\${PCHPREFIX}.pch") # make source files use/depend on PCH_NAME set_source_files_properties(${${SRCS}} PROPERTIES COMPILE_FLAGS "/Yu${PCHPREFIX}.h /FI${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_DEPENDS "${PCH_NAME}") From 34be23373b9e73694c3b214ba857283bad65aedb Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 9 Apr 2020 15:44:20 -0400 Subject: [PATCH 73/88] Handle more cases in dead member elim (#3289) * Handle more cases in dead member elim - Rewrite composite insert and extract operations on SpecConstnatOp. - Leaves assert for Access chain instructions, which are only allowed for kernels. - Other operations do not require any extra code will no longer cause an assert. Fixes #3284. Fixes #3282. --- source/opt/eliminate_dead_members_pass.cpp | 90 ++++++++++++++++---- test/opt/eliminate_dead_member_test.cpp | 99 ++++++++++++++++++++++ 2 files changed, 171 insertions(+), 18 deletions(-) diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp index 0b73b2dbcb..5b8f4ec54f 100644 --- a/source/opt/eliminate_dead_members_pass.cpp +++ b/source/opt/eliminate_dead_members_pass.cpp @@ -19,6 +19,7 @@ namespace { const uint32_t kRemovedMember = 0xFFFFFFFF; +const uint32_t kSpecConstOpOpcodeIdx = 0; } namespace spvtools { @@ -40,7 +41,22 @@ void EliminateDeadMembersPass::FindLiveMembers() { // we have to mark them as fully used just to be safe. for (auto& inst : get_module()->types_values()) { if (inst.opcode() == SpvOpSpecConstantOp) { - MarkTypeAsFullyUsed(inst.type_id()); + switch (inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) { + case SpvOpCompositeExtract: + MarkMembersAsLiveForExtract(&inst); + break; + case SpvOpCompositeInsert: + // Nothing specific to do. + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + assert(false && "Not implemented yet."); + break; + default: + break; + } } else if (inst.opcode() == SpvOpVariable) { switch (inst.GetSingleWordInOperand(0)) { case SpvStorageClassInput: @@ -153,13 +169,17 @@ void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory( void EliminateDeadMembersPass::MarkMembersAsLiveForExtract( const Instruction* inst) { - assert(inst->opcode() == SpvOpCompositeExtract); + assert(inst->opcode() == SpvOpCompositeExtract || + (inst->opcode() == SpvOpSpecConstantOp && + inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) == + SpvOpCompositeExtract)); - uint32_t composite_id = inst->GetSingleWordInOperand(0); + uint32_t first_operand = (inst->opcode() == SpvOpSpecConstantOp ? 1 : 0); + uint32_t composite_id = inst->GetSingleWordInOperand(first_operand); Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); uint32_t type_id = composite_inst->type_id(); - for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); uint32_t member_idx = inst->GetSingleWordInOperand(i); switch (type_inst->opcode()) { @@ -295,10 +315,22 @@ bool EliminateDeadMembersPass::RemoveDeadMembers() { modified |= UpdateOpArrayLength(inst); break; case SpvOpSpecConstantOp: - assert(false && "Not yet implemented."); - // with OpCompositeExtract, OpCompositeInsert - // For kernels: OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, - // OpInBoundsPtrAccessChain + switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) { + case SpvOpCompositeExtract: + modified |= UpdateCompsiteExtract(inst); + break; + case SpvOpCompositeInsert: + modified |= UpdateCompositeInsert(inst); + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + assert(false && "Not implemented yet."); + break; + default: + break; + } break; default: break; @@ -393,7 +425,8 @@ bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) { } bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) { - assert(inst->opcode() == SpvOpConstantComposite || + assert(inst->opcode() == SpvOpSpecConstantComposite || + inst->opcode() == SpvOpConstantComposite || inst->opcode() == SpvOpCompositeConstruct); uint32_t type_id = inst->type_id(); @@ -506,14 +539,25 @@ uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id, } bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { - uint32_t object_id = inst->GetSingleWordInOperand(0); + assert(inst->opcode() == SpvOpCompositeExtract || + (inst->opcode() == SpvOpSpecConstantOp && + inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) == + SpvOpCompositeExtract)); + + uint32_t first_operand = 0; + if (inst->opcode() == SpvOpSpecConstantOp) { + first_operand = 1; + } + uint32_t object_id = inst->GetSingleWordInOperand(first_operand); Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); uint32_t type_id = object_inst->type_id(); Instruction::OperandList new_operands; bool modified = false; - new_operands.emplace_back(inst->GetInOperand(0)); - for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + for (uint32_t i = 0; i < first_operand + 1; i++) { + new_operands.emplace_back(inst->GetInOperand(i)); + } + for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) { uint32_t member_idx = inst->GetSingleWordInOperand(i); uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); assert(new_member_idx != kRemovedMember); @@ -526,8 +570,6 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case SpvOpTypeStruct: - assert(i != 1 || (inst->opcode() != SpvOpPtrAccessChain && - inst->opcode() != SpvOpInBoundsPtrAccessChain)); // The type will have already been rewriten, so use the new member // index. type_id = type_inst->GetSingleWordInOperand(new_member_idx); @@ -552,15 +594,27 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { } bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) { - uint32_t composite_id = inst->GetSingleWordInOperand(1); + assert(inst->opcode() == SpvOpCompositeInsert || + (inst->opcode() == SpvOpSpecConstantOp && + inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) == + SpvOpCompositeInsert)); + + uint32_t first_operand = 0; + if (inst->opcode() == SpvOpSpecConstantOp) { + first_operand = 1; + } + + uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1); Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); uint32_t type_id = composite_inst->type_id(); Instruction::OperandList new_operands; bool modified = false; - new_operands.emplace_back(inst->GetInOperand(0)); - new_operands.emplace_back(inst->GetInOperand(1)); - for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + + for (uint32_t i = 0; i < first_operand + 2; ++i) { + new_operands.emplace_back(inst->GetInOperand(i)); + } + for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) { uint32_t member_idx = inst->GetSingleWordInOperand(i); uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); if (new_member_idx == kRemovedMember) { diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp index b6925d7d78..a9b0f28c74 100644 --- a/test/opt/eliminate_dead_member_test.cpp +++ b/test/opt/eliminate_dead_member_test.cpp @@ -1085,4 +1085,103 @@ TEST_F(EliminateDeadMemberTest, DontChangeOutputStructs) { EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); } +TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpExtract) { + // Test that an extract in an OpSpecConstantOp is correctly updated. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1 +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK: %type__Globals = OpTypeStruct %uint +; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]] +; CHECK: OpSpecConstantOp %uint CompositeExtract [[struct]] 0 + OpCapability Shader + OpCapability Addresses + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %main "main" + OpDecorate %c_0 SpecId 0 + OpDecorate %c_1 SpecId 1 + OpDecorate %c_2 SpecId 2 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + %uint = OpTypeInt 32 0 + %c_0 = OpSpecConstant %uint 0 + %c_1 = OpSpecConstant %uint 1 + %c_2 = OpSpecConstant %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 +%type__Globals = OpTypeStruct %uint %uint %uint +%spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2 +%extract = OpSpecConstantOp %uint CompositeExtract %spec_const_global 1 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %main = OpFunction %void None %14 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpInsert) { + // Test that an insert in an OpSpecConstantOp is correctly updated. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1 +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK: %type__Globals = OpTypeStruct %uint +; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]] +; CHECK: OpSpecConstantOp %type__Globals CompositeInsert %uint_3 [[struct]] 0 + OpCapability Shader + OpCapability Addresses + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %main "main" + OpDecorate %c_0 SpecId 0 + OpDecorate %c_1 SpecId 1 + OpDecorate %c_2 SpecId 2 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + %uint = OpTypeInt 32 0 + %c_0 = OpSpecConstant %uint 0 + %c_1 = OpSpecConstant %uint 1 + %c_2 = OpSpecConstant %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 +%type__Globals = OpTypeStruct %uint %uint %uint +%spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2 +%insert = OpSpecConstantOp %type__Globals CompositeInsert %uint_3 %spec_const_global 1 +%extract = OpSpecConstantOp %uint CompositeExtract %insert 1 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %main = OpFunction %void None %14 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + } // namespace From c531099eb345b5eedaecd1f9fc91eced9346d4ea Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 13 Apr 2020 09:24:47 -0400 Subject: [PATCH 74/88] Update acorn version (#3294) --- tools/sva/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock index be19e7ca6b..11ba12f734 100644 --- a/tools/sva/yarn.lock +++ b/tools/sva/yarn.lock @@ -47,9 +47,9 @@ acorn-jsx@^5.0.2: integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== acorn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" - integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== ajv@6.5.3: version "6.5.3" From 000040e707a0738f5c3cb9555e629c8381e24369 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Mon, 13 Apr 2020 09:29:36 -0400 Subject: [PATCH 75/88] Preserve debug info in eliminate-dead-functions (#3251) * Preserve debug info in eliminate-dead-functions The elimination of dead functions makes OpFunction operand of DebugFunction invalid. This commit replaces the operand with DebugInfoNone. --- source/opt/feature_manager.cpp | 7 ++ source/opt/feature_manager.h | 8 ++ source/opt/function.cpp | 13 ++- source/opt/instruction.cpp | 20 +++- source/opt/instruction.h | 13 ++- source/opt/ir_context.cpp | 62 +++++++++++ source/opt/ir_context.h | 16 ++- source/opt/type_manager.h | 7 ++ test/opt/eliminate_dead_functions_test.cpp | 69 ++++++++++++ test/opt/ir_context_test.cpp | 118 +++++++++++++++++++++ 10 files changed, 319 insertions(+), 14 deletions(-) diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index b4d6f1ba52..ad70c1e4f1 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -78,6 +78,8 @@ void FeatureManager::AddCapabilities(Module* module) { void FeatureManager::AddExtInstImportIds(Module* module) { extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); + extinst_importid_OpenCL100DebugInfo_ = + module->GetExtInstImportId("OpenCL.DebugInfo.100"); } bool operator==(const FeatureManager& a, const FeatureManager& b) { @@ -100,6 +102,11 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) { return false; } + if (a.extinst_importid_OpenCL100DebugInfo_ != + b.extinst_importid_OpenCL100DebugInfo_) { + return false; + } + return true; } } // namespace opt diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index 881d5e601a..66d1cbac33 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h @@ -51,6 +51,10 @@ class FeatureManager { return extinst_importid_GLSLstd450_; } + uint32_t GetExtInstImportId_OpenCL100DebugInfo() const { + return extinst_importid_OpenCL100DebugInfo_; + } + friend bool operator==(const FeatureManager& a, const FeatureManager& b); friend bool operator!=(const FeatureManager& a, const FeatureManager& b) { return !(a == b); @@ -84,6 +88,10 @@ class FeatureManager { // Common external instruction import ids, cached for performance. uint32_t extinst_importid_GLSLstd450_ = 0; + + // Common OpenCL100DebugInfo external instruction import ids, cached + // for performance. + uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; }; } // namespace opt diff --git a/source/opt/function.cpp b/source/opt/function.cpp index 5d50f37c60..2b7b4fb809 100644 --- a/source/opt/function.cpp +++ b/source/opt/function.cpp @@ -84,9 +84,12 @@ bool Function::WhileEachInst(const std::function& f, } } - for (auto& di : debug_insts_in_header_) { - if (!di.WhileEachInst(f, run_on_debug_line_insts)) { - return false; + if (!debug_insts_in_header_.empty()) { + Instruction* di = &debug_insts_in_header_.front(); + while (di != nullptr) { + Instruction* next_instruction = di->NextNode(); + if (!di->WhileEachInst(f, run_on_debug_line_insts)) return false; + di = next_instruction; } } @@ -118,9 +121,9 @@ bool Function::WhileEachInst(const std::function& f, } for (const auto& di : debug_insts_in_header_) { - if (!di.WhileEachInst(f, run_on_debug_line_insts)) { + if (!static_cast(&di)->WhileEachInst( + f, run_on_debug_line_insts)) return false; - } } for (const auto& bb : blocks_) { diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 3ce38a9a7e..a9d778ae60 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -33,6 +33,8 @@ const uint32_t kVariableStorageClassIndex = 0; const uint32_t kTypeImageSampledIndex = 5; // Constants for OpenCL.DebugInfo.100 extension instructions. +const uint32_t kExtInstSetIdInIdx = 0; +const uint32_t kExtInstInstructionInIdx = 1; const uint32_t kDebugScopeNumWords = 7; const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6; const uint32_t kDebugNoScopeNumWords = 5; @@ -510,6 +512,21 @@ bool Instruction::IsValidBasePointer() const { return false; } +OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { + if (opcode() != SpvOpExtInst) return OpenCLDebugInfo100InstructionsMax; + + if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) + return OpenCLDebugInfo100InstructionsMax; + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) != + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + return OpenCLDebugInfo100InstructionsMax; + } + + return OpenCLDebugInfo100Instructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + bool Instruction::IsValidBaseImage() const { uint32_t tid = type_id(); if (tid == 0) { @@ -714,9 +731,6 @@ bool Instruction::IsScalarizable() const { return true; } - const uint32_t kExtInstSetIdInIdx = 0; - const uint32_t kExtInstInstructionInIdx = 1; - if (opcode() == SpvOpExtInst) { uint32_t instSetId = context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); diff --git a/source/opt/instruction.h b/source/opt/instruction.h index a3342c616d..008a831bb6 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -22,14 +22,14 @@ #include #include +#include "OpenCLDebugInfo100.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_spirv_header.h" #include "source/opcode.h" #include "source/operand.h" +#include "source/opt/reflect.h" #include "source/util/ilist_node.h" #include "source/util/small_vector.h" - -#include "source/latest_version_glsl_std_450_header.h" -#include "source/latest_version_spirv_header.h" -#include "source/opt/reflect.h" #include "spirv-tools/libspirv.h" const uint32_t kNoDebugScope = 0; @@ -496,6 +496,11 @@ class Instruction : public utils::IntrusiveNodeBase { // rules for physical addressing. bool IsValidBasePointer() const; + // Returns debug opcode of an OpenCL.100.DebugInfo instruction. If + // it is not an OpenCL.100.DebugInfo instruction, just returns + // OpenCLDebugInfo100InstructionsMax. + OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; + // Dump this instruction on stderr. Useful when running interactive // debuggers. void Dump() const; diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 72993fd647..861ca2caa0 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -16,6 +16,7 @@ #include +#include "OpenCLDebugInfo100.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/log.h" #include "source/opt/mem_pass.h" @@ -29,6 +30,10 @@ static const int kSpvDecorateBuiltinInIdx = 2; static const int kEntryPointInterfaceInIdx = 3; static const int kEntryPointFunctionIdInIdx = 1; +// Constants for OpenCL.DebugInfo.100 extension instructions. +static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; + } // anonymous namespace namespace spvtools { @@ -153,6 +158,8 @@ Instruction* IRContext::KillInst(Instruction* inst) { KillNamesAndDecorates(inst); + KillOperandFromDebugInstructions(inst); + if (AreAnalysesValid(kAnalysisDefUse)) { get_def_use_mgr()->ClearInst(inst); } @@ -365,6 +372,61 @@ void IRContext::KillNamesAndDecorates(Instruction* inst) { KillNamesAndDecorates(rId); } +Instruction* IRContext::GetOpenCL100DebugInfoNone() { + if (debug_info_none_inst_) return debug_info_none_inst_; + assert(get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() && + "Module does not include debug info extension instruction."); + + // Create a new DebugInfoNone. + std::unique_ptr dbg_info_none(new Instruction( + this, SpvOpExtInst, get_type_mgr()->GetVoidTypeId(), TakeNextId(), + { + {SPV_OPERAND_TYPE_RESULT_ID, + {get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugInfoNone)}}, + })); + + // Add to the front of |ext_inst_debuginfo_|. + debug_info_none_inst_ = module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(dbg_info_none)); + return debug_info_none_inst_; +} + +void IRContext::KillOperandFromDebugInstructions(Instruction* inst) { + const auto opcode = inst->opcode(); + const uint32_t id = inst->result_id(); + // Kill id of OpFunction from DebugFunction. + if (opcode == SpvOpFunction) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugFunction) + continue; + auto& operand = it->GetOperand(kDebugFunctionOperandFunctionIndex); + if (operand.words[0] == id) { + operand.words[0] = GetOpenCL100DebugInfoNone()->result_id(); + } + } + } + // Kill id of OpVariable for global variable from DebugGlobalVariable. + if (opcode == SpvOpVariable || IsConstantInst(opcode)) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() != + OpenCLDebugInfo100DebugGlobalVariable) + continue; + auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex); + if (operand.words[0] == id) { + operand.words[0] = GetOpenCL100DebugInfoNone()->result_id(); + } + } + } + // Notice that we do not need anythings to do for local variables. + // DebugLocalVariable does not have an OpVariable operand. Instead, + // DebugDeclare/DebugValue has an OpVariable operand for a local + // variable. The function inlining pass handles it properly. +} + void IRContext::AddCombinatorsForCapability(uint32_t capability) { if (capability == SpvCapabilityShader) { combinator_ops_[0].insert({SpvOpNop, diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 723a2bbb62..b275ae4ac9 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -102,7 +102,8 @@ class IRContext { id_to_name_(nullptr), max_id_bound_(kDefaultMaxIdBound), preserve_bindings_(false), - preserve_spec_constants_(false) { + preserve_spec_constants_(false), + debug_info_none_inst_(nullptr) { SetContextMessageConsumer(syntax_context_, consumer_); module_->SetContext(this); } @@ -119,7 +120,8 @@ class IRContext { id_to_name_(nullptr), max_id_bound_(kDefaultMaxIdBound), preserve_bindings_(false), - preserve_spec_constants_(false) { + preserve_spec_constants_(false), + debug_info_none_inst_(nullptr) { SetContextMessageConsumer(syntax_context_, consumer_); module_->SetContext(this); InitializeCombinators(); @@ -426,6 +428,9 @@ class IRContext { // Kill all name and decorate ops targeting the result id of |inst|. void KillNamesAndDecorates(Instruction* inst); + // Change operands of debug instruction to DebugInfoNone. + void KillOperandFromDebugInstructions(Instruction* inst); + // Returns the next unique id for use by an instruction. inline uint32_t TakeNextUniqueId() { assert(unique_id_ != std::numeric_limits::max()); @@ -705,6 +710,9 @@ class IRContext { // Add |var_id| to all entry points in module. void AddVarToEntryPoints(uint32_t var_id); + // Get the existing DebugInfoNone. If it is null, create one and keep it. + Instruction* GetOpenCL100DebugInfoNone(); + // The SPIR-V syntax context containing grammar tables for opcodes and // operands. spv_context syntax_context_; @@ -798,6 +806,10 @@ class IRContext { // Whether all specialization constants within |module_| // should be preserved. bool preserve_spec_constants_; + + // DebugInfoNone instruction. We need only a single DebugInfoNone. + // To reuse the existing one, we keep it using this member variable. + Instruction* debug_info_none_inst_; }; inline IRContext::Analysis operator|(IRContext::Analysis lhs, diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h index 8fcf8aa632..ce9d83d410 100644 --- a/source/opt/type_manager.h +++ b/source/opt/type_manager.h @@ -194,6 +194,13 @@ class TypeManager { uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); } + Type* GetVoidType() { + Void void_type; + return GetRegisteredType(&void_type); + } + + uint32_t GetVoidTypeId() { return GetTypeInstruction(GetVoidType()); } + private: using TypeToIdMap = std::unordered_map; diff --git a/test/opt/eliminate_dead_functions_test.cpp b/test/opt/eliminate_dead_functions_test.cpp index 0a3d490a8f..54746739f7 100644 --- a/test/opt/eliminate_dead_functions_test.cpp +++ b/test/opt/eliminate_dead_functions_test.cpp @@ -204,6 +204,75 @@ OpFunctionEnd /* skip_nop = */ true); } +TEST_F(EliminateDeadFunctionsBasicTest, DebugRemoveFunctionFromDebugFunction) { + // We want to remove id of OpFunction from DebugFunction. + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 %4 +OpExecutionMode %2 OriginUpperLeft +%5 = OpString "ps.hlsl" +OpSource HLSL 600 %5 "float4 foo() { + return 1; +} +float4 main(float4 color : COLOR) : SV_TARGET { + return foo() + color; +} +" +%6 = OpString "float" +%7 = OpString "main" +%8 = OpString "foo" +; CHECK: [[foo:%\d+]] = OpString "foo" +OpDecorate %3 Location 0 +OpDecorate %4 Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%v4float = OpTypeVector %float 4 +%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%18 = OpTypeFunction %void +%19 = OpTypeFunction %v4float +%3 = OpVariable %_ptr_Input_v4float Input +%4 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK: [[info_none:%\d+]] = OpExtInst %void %1 DebugInfoNone +%20 = OpExtInst %void %1 DebugSource %5 +%21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL +%22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float +%23 = OpExtInst %void %1 DebugTypeVector %22 4 +%24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23 +%25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 +%26 = OpExtInst %void %1 DebugFunction %7 %24 %20 4 1 %21 %7 FlagIsProtected|FlagIsPrivate 4 %2 +%27 = OpExtInst %void %1 DebugFunction %8 %25 %20 1 1 %21 %8 FlagIsProtected|FlagIsPrivate 1 %28 +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugFunction [[foo]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[info_none]] +%29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %27 +%40 = OpExtInst %void %1 DebugInlinedAt 4 %26 +%2 = OpFunction %void None %18 +%30 = OpLabel +%39 = OpVariable %_ptr_Function_v4float Function +%41 = OpExtInst %void %1 DebugScope %27 %40 +OpStore %39 %14 +%32 = OpLoad %v4float %39 +%42 = OpExtInst %void %1 DebugScope %26 +%33 = OpLoad %v4float %3 +%34 = OpFAdd %v4float %32 %33 +OpStore %4 %34 +%43 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +%28 = OpFunction %v4float None %19 +%36 = OpLabel +OpReturnValue %14 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp index d5710fc469..e72561cd60 100644 --- a/test/opt/ir_context_test.cpp +++ b/test/opt/ir_context_test.cpp @@ -19,6 +19,7 @@ #include #include +#include "OpenCLDebugInfo100.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "source/opt/pass.h" @@ -372,6 +373,123 @@ TEST_F(IRContextTest, KillDecorationGroup) { EXPECT_TRUE(context->annotations().empty()); } +TEST_F(IRContextTest, KillFunctionFromDebugFunction) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpString "ps.hlsl" + %4 = OpString "foo" + OpSource HLSL 600 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %7 = OpExtInst %void %1 DebugSource %3 + %8 = OpExtInst %void %1 DebugCompilationUnit 1 4 %7 HLSL + %9 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void + %10 = OpExtInst %void %1 DebugFunction %4 %9 %7 1 1 %8 %4 FlagIsProtected|FlagIsPrivate 1 %11 + %2 = OpFunction %void None %6 + %12 = OpLabel + OpReturn + OpFunctionEnd + %11 = OpFunction %void None %6 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Delete the second variable. + context->KillDef(11); + + // Get DebugInfoNone id. + uint32_t debug_info_none_id = 0; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + debug_info_none_id = it->result_id(); + } + } + EXPECT_NE(0, debug_info_none_id); + + // Check the Function operand of DebugFunction is DebugInfoNone. + const uint32_t kDebugFunctionOperandFunctionIndex = 13; + bool checked = false; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + EXPECT_FALSE(checked); + EXPECT_EQ(it->GetOperand(kDebugFunctionOperandFunctionIndex).words[0], + debug_info_none_id); + checked = true; + } + } + EXPECT_TRUE(checked); +} + +TEST_F(IRContextTest, KillVariableFromDebugGlobalVariable) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpString "ps.hlsl" + %4 = OpString "foo" + %5 = OpString "int" + OpSource HLSL 600 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Private_uint = OpTypePointer Private %uint + %void = OpTypeVoid + %10 = OpTypeFunction %void + %11 = OpVariable %_ptr_Private_uint Private + %12 = OpExtInst %void %1 DebugSource %3 + %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL + %14 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 Signed + %15 = OpExtInst %void %1 DebugGlobalVariable %4 %14 %12 1 12 %13 %4 %11 FlagIsDefinition + %2 = OpFunction %void None %10 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Delete the second variable. + context->KillDef(11); + + // Get DebugInfoNone id. + uint32_t debug_info_none_id = 0; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + debug_info_none_id = it->result_id(); + } + } + EXPECT_NE(0, debug_info_none_id); + + // Check the Function operand of DebugFunction is DebugInfoNone. + const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; + bool checked = false; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugGlobalVariable) { + EXPECT_FALSE(checked); + EXPECT_EQ( + it->GetOperand(kDebugGlobalVariableOperandVariableIndex).words[0], + debug_info_none_id); + checked = true; + } + } + EXPECT_TRUE(checked); +} + TEST_F(IRContextTest, BasicVisitFromEntryPoint) { // Make sure we visit the entry point, and the function it calls. // Do not visit Dead or Exported. From e70d25f6fa5d6a560f621aaaf6505472be8cc069 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 13 Apr 2020 10:08:31 -0400 Subject: [PATCH 76/88] Struct CFG analysus and single block loop (#3293) Loop headers must be marked as in the continue if the loop header is also the continue target. Fixes #3264 --- source/opt/struct_cfg_analysis.cpp | 7 ++++- test/opt/struct_cfg_analysis_test.cpp | 29 ++++++++++++++++++ test/opt/wrap_opkill_test.cpp | 42 +++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/source/opt/struct_cfg_analysis.cpp b/source/opt/struct_cfg_analysis.cpp index b16322c1fa..57fc49c1cf 100644 --- a/source/opt/struct_cfg_analysis.cpp +++ b/source/opt/struct_cfg_analysis.cpp @@ -85,9 +85,14 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) { if (merge_inst->opcode() == SpvOpLoopMerge) { new_state.cinfo.containing_loop = block->id(); new_state.cinfo.containing_switch = 0; - new_state.cinfo.in_continue = false; new_state.continue_node = merge_inst->GetSingleWordInOperand(kContinueNodeIndex); + if (block->id() == new_state.continue_node) { + new_state.cinfo.in_continue = true; + bb_to_construct_[block->id()].in_continue = true; + } else { + new_state.cinfo.in_continue = false; + } } else { new_state.cinfo.containing_loop = state.back().cinfo.containing_loop; new_state.cinfo.in_continue = state.back().cinfo.in_continue; diff --git a/test/opt/struct_cfg_analysis_test.cpp b/test/opt/struct_cfg_analysis_test.cpp index 0451a8b46c..2d1deecca8 100644 --- a/test/opt/struct_cfg_analysis_test.cpp +++ b/test/opt/struct_cfg_analysis_test.cpp @@ -1369,6 +1369,35 @@ TEST_F(StructCFGAnalysisTest, FuncCallInContinueIndirect) { auto c = analysis.FindFuncsCalledFromContinue(); EXPECT_THAT(c, UnorderedElementsAre(14u, 16u, 21u)); } + +TEST_F(StructCFGAnalysisTest, SingleBlockLoop) { + const std::string text = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %bool = OpTypeBool + %undef = OpUndef %bool + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %2 = OpLabel + OpBranch %3 + %3 = OpLabel + OpLoopMerge %4 %3 None + OpBranchConditional %undef %3 %4 + %4 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + EXPECT_TRUE(analysis.IsInContinueConstruct(3)); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/wrap_opkill_test.cpp b/test/opt/wrap_opkill_test.cpp index b6b6a23210..359a9f2428 100644 --- a/test/opt/wrap_opkill_test.cpp +++ b/test/opt/wrap_opkill_test.cpp @@ -547,6 +547,48 @@ OpFunctionEnd EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); } +TEST_F(WrapOpKillTest, KillInSingleBlockLoop) { + const std::string text = R"( +; CHECK: OpFunction %void +; CHECK: OpFunction %void +; CHECK-NOT: OpKill +; CHECK: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NOT: OpKill +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %bool = OpTypeBool + %undef = OpUndef %bool + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn +%main_entry = OpLabel + OpBranch %loop + %loop = OpLabel + %call = OpFunctionCall %void %sub + OpLoopMerge %exit %loop None + OpBranchConditional %undef %loop %exit + %exit = OpLabel + OpReturn + OpFunctionEnd + %sub = OpFunction %void None %void_fn + %sub_entry = OpLabel + OpSelectionMerge %ret None + OpBranchConditional %undef %kill %ret + %kill = OpLabel + OpKill + %ret = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + } // namespace } // namespace opt } // namespace spvtools From ca5751590ed751fba8abaae9b345663002878865 Mon Sep 17 00:00:00 2001 From: David Neto Date: Tue, 14 Apr 2020 08:28:25 -0700 Subject: [PATCH 77/88] If SPIRV-Headers is in our tree, include it as subproject (#3299) This allows enclosing projects to use SPIRV-Headers_SOURCE_DIR to set up in header includes. --- external/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 8bde13ca15..3c7b40331d 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -25,7 +25,17 @@ else() endif() if (IS_DIRECTORY ${SPIRV_HEADER_DIR}) + # TODO(dneto): We should not be modifying the parent scope. set(SPIRV_HEADER_INCLUDE_DIR ${SPIRV_HEADER_DIR}/include PARENT_SCOPE) + + # Add SPIRV-Headers as a sub-project if it isn't already defined. + # Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find + # headers to include. + if (NOT DEFINED SPIRV-Headers_SOURCE_DIR) + set(SPIRV_HEADERS_SKIP_INSTALL ON) + set(SPIRV_HEADERS_SKIP_EXAMPLES ON) + add_subdirectory(${SPIRV_HEADER_DIR}) + endif() else() message(FATAL_ERROR "SPIRV-Headers was not found - please checkout a copy under external/.") From 495664489442ae99d283c5cfb191c6777d562c21 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 14 Apr 2020 12:57:06 -0400 Subject: [PATCH 78/88] Add tests for recently added command line option (#3297) We have not added tests for the new command line options recently. I've updated the test and fixed on option that was incorrect. Fixes #3247 --- source/opt/optimizer.cpp | 2 +- test/tools/opt/flags.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 0a937e8577..f11dc346f6 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -498,7 +498,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "legalize-vector-shuffle") { RegisterPass(CreateLegalizeVectorShufflePass()); } else if (pass_name == "split-invalid-unreachable") { - RegisterPass(CreateLegalizeVectorShufflePass()); + RegisterPass(CreateSplitInvalidUnreachablePass()); } else if (pass_name == "decompose-initialized-variables") { RegisterPass(CreateDecomposeInitializedVariablesPass()); } else if (pass_name == "graphics-robust-access") { diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index 2f6c0a7937..8aaaf312cd 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py @@ -73,7 +73,10 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, '--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite', '--scalar-replacement', '--scalar-replacement=42', '--strength-reduction', '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209', - '--unify-const' + '--unify-const', '--legalize-vector-shuffle', + '--split-invalid-unreachable', '--generate-webgpu-initializers', + '--decompose-initialized-variables', '--graphics-robust-access', + '--wrap-opkill', '--amd-ext-to-khr' ] expected_passes = [ 'wrap-opkill', @@ -120,7 +123,14 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, 'strip-reflect', 'vector-dce', 'workaround-1209', - 'unify-const' + 'unify-const', + 'legalize-vector-shuffle', + 'split-invalid-unreachable', + 'generate-webgpu-initializers', + 'decompose-initialized-variables', + 'graphics-robust-access', + 'wrap-opkill', + 'amd-ext-to-khr' ] shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') output = placeholder.TempFileName('output.spv') From 2a2bdbd5d7215128641d5bfecfc535bb396ec1f4 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Tue, 14 Apr 2020 12:57:48 -0400 Subject: [PATCH 79/88] Remove implicit fallthrough (#3298) Fixes #3296 * Make OpReturn its own case fully --- source/val/validate_cfg.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index 1c279f6549..1e33e51054 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -1090,8 +1090,9 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { return _.diag(SPV_ERROR_INVALID_CFG, inst) << "OpReturn can only be called from a function with void " << "return type."; + _.current_function().RegisterBlockEnd(std::vector(), opcode); + break; } - // Fallthrough. case SpvOpKill: case SpvOpReturnValue: case SpvOpUnreachable: From 7d65bce0bbe3898adef6f008c94752a21932f9f1 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 14 Apr 2020 12:58:05 -0400 Subject: [PATCH 80/88] Sampled images as read-only storage (#3295) There are some cases where a variable that is declared as a sampled image could be read only. That is when the image type has sampled == 1. Fixes #3288 --- source/opt/instruction.cpp | 21 +++++++++++++++-- test/opt/value_table_test.cpp | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index a9d778ae60..9e54e7e8d7 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -182,10 +182,27 @@ void Instruction::ReplaceOperands(const OperandList& new_operands) { bool Instruction::IsReadOnlyLoad() const { if (IsLoad()) { Instruction* address_def = GetBaseAddress(); - if (!address_def || address_def->opcode() != SpvOpVariable) { + if (!address_def) { return false; } - return address_def->IsReadOnlyVariable(); + + if (address_def->opcode() == SpvOpVariable) { + if (address_def->IsReadOnlyVariable()) { + return true; + } + } + + if (address_def->opcode() == SpvOpLoad) { + const analysis::Type* address_type = + context()->get_type_mgr()->GetType(address_def->type_id()); + if (address_type->AsSampledImage() != nullptr) { + const auto* image_type = + address_type->AsSampledImage()->image_type()->AsImage(); + if (image_type->sampled() == 1) { + return true; + } + } + } } return false; } diff --git a/test/opt/value_table_test.cpp b/test/opt/value_table_test.cpp index a0942ccdc0..76e7f73a2d 100644 --- a/test/opt/value_table_test.cpp +++ b/test/opt/value_table_test.cpp @@ -684,6 +684,50 @@ TEST_F(ValueTableTest, EmptyPhiTest) { vtable.GetValueNumber(inst); } +TEST_F(ValueTableTest, RedundantSampledImageLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %tex0 "tex0" + OpName %gl_FragColor "gl_FragColor" + OpDecorate %tex0 Location 0 + OpDecorate %tex0 DescriptorSet 0 + OpDecorate %tex0 Binding 0 + OpDecorate %gl_FragColor Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %9 = OpTypeImage %float 2D 0 0 0 1 Unknown + %10 = OpTypeSampledImage %9 +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %tex0 = OpVariable %_ptr_UniformConstant_10 UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %13 = OpConstantNull %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output + %14 = OpUndef %v4float + %main = OpFunction %void None %6 + %15 = OpLabel + %16 = OpLoad %10 %tex0 + %17 = OpImageSampleProjImplicitLod %v4float %16 %13 + %18 = OpImageSampleProjImplicitLod %v4float %16 %13 + %19 = OpFAdd %v4float %18 %17 + OpStore %gl_FragColor %19 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* load1 = context->get_def_use_mgr()->GetDef(17); + Instruction* load2 = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2)); +} + } // namespace } // namespace opt } // namespace spvtools From 7ce2db1763b012e11098902302503365ce104b7a Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 14 Apr 2020 20:17:05 +0100 Subject: [PATCH 81/88] spirv-fuzz: Fix comment. (#3300) --- source/fuzz/fuzzer_pass_donate_modules.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index e61572c53e..c59ad71d13 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -66,7 +66,7 @@ class FuzzerPassDonateModules : public FuzzerPass { opt::IRContext* donor_ir_context, std::map* original_id_to_donated_id); - // TODO comment + // Helper method for HandleTypesAndValues, to handle a single type/value. void HandleTypeOrValue( const opt::Instruction& type_or_value, std::map* original_id_to_donated_id); From f82d47003e7df3afd487b1727bfdedaeaabd4f42 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 14 Apr 2020 20:17:42 +0100 Subject: [PATCH 82/88] spirv-fuzz: Respect rules for OpSampledImage (#3287) The SPIR-V data rules say that all uses of an OpSampledImage instruction must be in the same block as the instruction, and highly restrict those instructions that can consume the result id of an OpSampledImage. This adapts the transformations that split blocks and create synonyms to avoid separating an OpSampledImage use from its definition, and to avoid synonym-creation instructions such as OpCopyObject consuming an OpSampledImage result id. --- source/fuzz/fuzzer_util.cpp | 6 ++ source/fuzz/transformation_split_block.cpp | 30 +++++++++- test/fuzz/fuzzer_replayer_test.cpp | 50 +++++++++++++++- test/fuzz/transformation_copy_object_test.cpp | 58 +++++++++++++++++++ test/fuzz/transformation_split_block_test.cpp | 56 ++++++++++++++++++ 5 files changed, 197 insertions(+), 3 deletions(-) diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 4d85984c5b..f09943fe68 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -218,6 +218,12 @@ bool CanInsertOpcodeBeforeInstruction( } bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) { + if (inst->opcode() == SpvOpSampledImage) { + // The SPIR-V data rules say that only very specific instructions may + // may consume the result id of an OpSampledImage, and this excludes the + // instructions that are used for making synonyms. + return false; + } if (!inst->HasResultId()) { // We can only make a synonym of an instruction that generates an id. return false; diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp index 3de081e036..b020d98a07 100644 --- a/source/fuzz/transformation_split_block.cpp +++ b/source/fuzz/transformation_split_block.cpp @@ -76,8 +76,34 @@ bool TransformationSplitBlock::IsApplicable( } // We cannot split before an OpPhi unless the OpPhi has exactly one // associated incoming edge. - return !(split_before->opcode() == SpvOpPhi && - split_before->NumInOperands() != 2); + if (split_before->opcode() == SpvOpPhi && + split_before->NumInOperands() != 2) { + return false; + } + + // Splitting the block must not separate the definition of an OpSampledImage + // from its use: the SPIR-V data rules require them to be in the same block. + std::set sampled_image_result_ids; + bool before_split = true; + for (auto& instruction : *block_to_split) { + if (&instruction == &*split_before) { + before_split = false; + } + if (before_split) { + if (instruction.opcode() == SpvOpSampledImage) { + sampled_image_result_ids.insert(instruction.result_id()); + } + } else { + if (!instruction.WhileEachInId( + [&sampled_image_result_ids](uint32_t* id) -> bool { + return !sampled_image_result_ids.count(*id); + })) { + return false; + } + } + } + + return true; } void TransformationSplitBlock::Apply( diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index b9024b9dbf..1e7c64323c 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -1553,6 +1553,47 @@ const std::string kTestShader5 = R"( OpFunctionEnd )"; +// Some miscellaneous SPIR-V. + +const std::string kTestShader6 = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + OpReturn + OpFunctionEnd + )"; + void AddConstantUniformFact(protobufs::FactSequence* facts, uint32_t descriptor_set, uint32_t binding, std::vector&& indices, uint32_t value) { @@ -1591,7 +1632,7 @@ void RunFuzzerAndReplayer(const std::string& shader, std::vector donor_suppliers; for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4, - &kTestShader5}) { + &kTestShader5, &kTestShader6}) { donor_suppliers.emplace_back([donor]() { return BuildModule(env, kConsoleMessageConsumer, *donor, kFuzzAssembleOption); @@ -1682,6 +1723,13 @@ TEST(FuzzerReplayerTest, Miscellaneous5) { kNumFuzzerRuns); } +TEST(FuzzerReplayerTest, Miscellaneous6) { + // Do some fuzzer runs, starting from an initial seed of 57 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader6, protobufs::FactSequence(), 57, + kNumFuzzerRuns); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp index fa5a5b12ee..22eecf63d1 100644 --- a/test/fuzz/transformation_copy_object_test.cpp +++ b/test/fuzz/transformation_copy_object_test.cpp @@ -718,6 +718,64 @@ TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) { transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); } +TEST(TransformationCopyObject, DoNotCopyOpSampledImage) { + // This checks that we do not try to copy the result id of an OpSampledImage + // instruction. + std::string shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + 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; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + ASSERT_FALSE( + TransformationCopyObject( + 216, MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), + 500) + .IsApplicable(context.get(), transformation_context)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_split_block_test.cpp b/test/fuzz/transformation_split_block_test.cpp index 8e9e8a4f13..30bac026c6 100644 --- a/test/fuzz/transformation_split_block_test.cpp +++ b/test/fuzz/transformation_split_block_test.cpp @@ -866,6 +866,62 @@ TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) { ASSERT_TRUE(IsEqual(env, after_split, context.get())); } +TEST(TransformationSplitBlockTest, DoNotSplitUseOfOpSampledImage) { + // This checks that we cannot split the definition of an OpSampledImage + // from its use. + std::string shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + 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; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + auto split = TransformationSplitBlock( + MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), 500); + ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context)); +} + } // namespace } // namespace fuzz } // namespace spvtools From 2f180468a71ca33c88e057843644039202260d71 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 14 Apr 2020 20:21:33 +0100 Subject: [PATCH 83/88] spirv-fuzz: Handle image storage class in donation (#3290) Demotes the image storage class to Private during donation. Also fixes an issue where instructions that depended on non-donated global values would not be handled properly. --- source/fuzz/fuzzer_pass_donate_modules.cpp | 10 ++- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 79 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index bb48303ef9..fc71d344c8 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -124,6 +124,7 @@ SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass( case SpvStorageClassUniform: case SpvStorageClassUniformConstant: case SpvStorageClassPushConstant: + case SpvStorageClassImage: // We change these to Private return SpvStorageClassPrivate; default: @@ -721,11 +722,12 @@ bool FuzzerPassDonateModules::CanDonateInstruction( // We do not have a mapped result id for this id operand. That either // means that it is a forward reference (which is OK), that we skipped // the instruction that generated it (which is not OK), or that it is - // the id of a function that we did not donate (which is not OK). We - // check for the latter two cases. + // the id of a function or global value that we did not donate (which + // is not OK). We check for the latter two cases. if (skipped_instructions.count(*in_id) || - donor_ir_context->get_def_use_mgr()->GetDef(*in_id)->opcode() == - SpvOpFunction) { + // A function or global value does not have an associated basic + // block. + !donor_ir_context->get_instr_block(*in_id)) { result = false; return false; } diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index 432ca4f8a5..0833c1d50e 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -1058,6 +1058,85 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) { ASSERT_TRUE(IsValid(env, recipient_context.get())); } +TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "MainPSPacked" + OpExecutionMode %2 OriginUpperLeft + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 128 + %49 = OpTypeInt 32 0 + %50 = OpTypeFloat 32 + %58 = OpConstant %50 1 + %66 = OpConstant %49 0 + %87 = OpTypeVector %50 2 + %88 = OpConstantComposite %87 %58 %58 + %17 = OpTypeImage %49 2D 2 0 0 2 R32ui + %118 = OpTypePointer UniformConstant %17 + %123 = OpTypeVector %49 2 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %142 = OpTypePointer Image %49 + %18 = OpVariable %118 UniformConstant + %2 = OpFunction %132 None %133 + %153 = OpLabel + %495 = OpConvertFToU %123 %88 + %501 = OpImageTexelPointer %142 %18 %495 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, recipient_context.get())); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, donor_context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(IsValid(env, recipient_context.get())); +} + TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { std::string recipient_shader = R"( OpCapability Shader From f460cca9dca66da49a43bd83804900e38087201a Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 14 Apr 2020 20:21:58 +0100 Subject: [PATCH 84/88] spirv-fuzz: Handle OpRuntimeArray when replacing ids with synonyms (#3292) Provides support for runtime arrays in the code that traverses composite types when checking applicability of transformations that replace ids with synonyms. --- ...transformation_replace_id_with_synonym.cpp | 3 + ...formation_replace_id_with_synonym_test.cpp | 116 ++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp index 87194c8eb7..89343b496a 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -125,6 +125,9 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym( } else if (composite_type_being_accessed->AsArray()) { composite_type_being_accessed = composite_type_being_accessed->AsArray()->element_type(); + } else if (composite_type_being_accessed->AsRuntimeArray()) { + composite_type_being_accessed = + composite_type_being_accessed->AsRuntimeArray()->element_type(); } else { assert(composite_type_being_accessed->AsStruct()); auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef( diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index d563afa75e..6b514a3acc 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -1277,6 +1277,122 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationReplaceIdWithSynonymTest, RuntimeArrayTest) { + // This checks that OpRuntimeArray is correctly handled. + 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 + OpDecorate %8 ArrayStride 8 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypeRuntimeArray %7 + %9 = OpTypeStruct %8 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpCopyObject %6 %12 + %51 = OpCopyObject %13 %14 + %16 = OpAccessChain %15 %11 %12 %12 %14 + OpStore %16 %12 + 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; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + // Add synonym fact relating %50 and %12. + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(50, 12), + context.get()); + // Add synonym fact relating %51 and %14. + transformation_context.GetFactManager()->AddFact(MakeSynonymFact(51, 14), + context.get()); + + // Not legal because the index being replaced is a struct index. + ASSERT_FALSE( + TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 1), + 50) + .IsApplicable(context.get(), transformation_context)); + + // Fine to replace an index into a runtime array. + auto replacement1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 2), + 50); + ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context)); + replacement1.Apply(context.get(), &transformation_context); + + // Fine to replace an index into a vector inside the runtime array. + auto replacement2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 14, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 3), + 51); + ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context)); + replacement2.Apply(context.get(), &transformation_context); + + ASSERT_TRUE(IsValid(env, context.get())); + + const 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 + OpDecorate %8 ArrayStride 8 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypeRuntimeArray %7 + %9 = OpTypeStruct %8 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpCopyObject %6 %12 + %51 = OpCopyObject %13 %14 + %16 = OpAccessChain %15 %11 %12 %50 %51 + OpStore %16 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools From c018fc6ae667b659c80b4357c20f6016ee3fe961 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 14 Apr 2020 20:22:30 +0100 Subject: [PATCH 85/88] spirv-fuzz: Do not outline regions that produce pointer outputs (#3291) The function outliner uses a struct to return ids that a region generates and that are used outside that region. If these ids have pointer type this would result in a struct with pointer members, which leads to illegal loading from non-logical pointers if logical addressing is used. This change bans that outlining possibility. --- .../fuzz/transformation_outline_function.cpp | 14 ++++- .../transformation_outline_function_test.cpp | 60 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 117cdc6f80..39a4b01420 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -274,12 +274,20 @@ bool TransformationOutlineFunction::IsApplicable( } // For each region output id -- i.e. every id defined inside the region but - // used outside the region -- there needs to be a corresponding fresh id that - // can hold the value for this id computed in the outlined function. + // used outside the region, ... std::map output_id_to_fresh_id_map = PairSequenceToMap(message_.output_id_to_fresh_id()); for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) { - if (output_id_to_fresh_id_map.count(id) == 0) { + if ( + // ... there needs to be a corresponding fresh id that can hold the + // value for this id computed in the outlined function, and ... + output_id_to_fresh_id_map.count(id) == 0 + // ... the output id must not have pointer type (to avoid creating a + // struct with pointer members to pass data out of the outlined + // function) + || ir_context->get_def_use_mgr() + ->GetDef(fuzzerutil::GetTypeId(ir_context, id)) + ->opcode() == SpvOpTypePointer) { return false; } } diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index 7beed85164..4b0f5ea7ec 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -2460,6 +2460,66 @@ TEST(TransformationOutlineFunctionTest, } } +TEST(TransformationOutlineFunctionTest, + DoNotOutlineCodeThatProducesUsedPointer) { + // This checks that we cannot outline a region of code if it produces a + // pointer result id that gets used outside the region. This avoids creating + // a struct with a pointer member. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %100 = OpTypeInt 32 0 + %99 = OpConstant %100 0 + %101 = OpTypeVector %100 2 + %102 = OpTypePointer Function %100 + %103 = OpTypePointer Function %101 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %104 = OpVariable %103 Function + OpBranch %80 + %80 = OpLabel + %105 = OpAccessChain %102 %104 %99 + OpBranch %106 + %106 = OpLabel + OpStore %105 %99 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + TransformationOutlineFunction transformation( + /*entry_block*/ 80, + /*exit_block*/ 80, + /*new_function_struct_return_type_id*/ 300, + /*new_function_type_id*/ 301, + /*new_function_id*/ 302, + /*new_function_region_entry_block*/ 304, + /*new_caller_result_id*/ 305, + /*new_callee_result_id*/ 306, + /*input_id_to_fresh_id*/ {{104, 307}}, + /*output_id_to_fresh_id*/ {{105, 308}}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + TEST(TransformationOutlineFunctionTest, Miscellaneous1) { // This tests outlining of some non-trivial code. From ed96301c6c4ab90c8dcdf5f4a11bec72854a5f95 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 15 Apr 2020 11:39:33 +0100 Subject: [PATCH 86/88] spirv-fuzz: Fix to outliner (#3302) Adds an extra condition on when a region can be outlined to avoid the case where a region ends with a loop head but such that the loop's continue target is in the region. (Outlining such a region would mean that the loop merge is in the original function and the continue target in the outlined function.) --- .../fuzz/transformation_outline_function.cpp | 21 +++++-- .../transformation_outline_function_test.cpp | 61 ++++++++++++++++++- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 39a4b01420..d84545a297 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -198,10 +198,23 @@ bool TransformationOutlineFunction::IsApplicable( for (auto& block : *entry_block->GetParent()) { if (&block == exit_block) { // It is OK (and typically expected) for the exit block of the region to - // have successors outside the region. It is also OK for the exit block - // to head a structured control flow construct - the block containing the - // call to the outlined function will end up heading this construct if - // outlining takes place. + // have successors outside the region. + // + // It is also OK for the exit block to head a structured control flow + // construct - the block containing the call to the outlined function will + // end up heading this construct if outlining takes place. However, we + // must ensure that if the exit block heads a loop, the continue target + // for this loop is outside the region. + if (auto loop_merge = block.GetLoopMergeInst()) { + // The exit block heads a loop + auto continue_target = + ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + if (region_set.count(continue_target)) { + // The continue target for the loop is in the region. + return false; + } + } + continue; } diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index 4b0f5ea7ec..01bba317a3 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -2465,7 +2465,6 @@ TEST(TransformationOutlineFunctionTest, // This checks that we cannot outline a region of code if it produces a // pointer result id that gets used outside the region. This avoids creating // a struct with a pointer member. - std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -2520,6 +2519,66 @@ TEST(TransformationOutlineFunctionTest, transformation.IsApplicable(context.get(), transformation_context)); } +TEST(TransformationOutlineFunctionTest, + ExitBlockHeadsLoopButContinueConstructIsInRegion) { + // This checks that it is not possible outline a region that ends in a loop + // head if the continue target for the loop is inside the region. + 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 + %15 = OpTypeInt 32 1 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %40 = OpConstantTrue %35 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %15 %39 %22 %39 %25 + OpLoopMerge %26 %25 None + OpBranchConditional %40 %25 %26 + %25 = OpLabel + OpBranch %23 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + TransformationOutlineFunction transformation( + /*entry_block*/ 22, + /*exit_block*/ 23, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); +} + TEST(TransformationOutlineFunctionTest, Miscellaneous1) { // This tests outlining of some non-trivial code. From 61b7de3c39f01a0eeb717f444c86990547752e26 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Wed, 15 Apr 2020 14:41:52 -0400 Subject: [PATCH 87/88] Remove unreachable code. (#3304) --- source/opt/graphics_robust_access_pass.cpp | 4 ++++ source/opt/ir_context.cpp | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index 22c979cf3a..db14020db2 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp @@ -802,8 +802,11 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( opt::Instruction* image_texel_pointer) { // TODO(dneto): Write tests for this code. // TODO(dneto): Use signed-clamp + (void)(image_texel_pointer); return SPV_SUCCESS; + // Do not compile this code until it is ready to be used. +#if 0 // Example: // %texel_ptr = OpImageTexelPointer %texel_ptr_type %image_ptr %coord // %sample @@ -1035,6 +1038,7 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( def_use_mgr->AnalyzeInstUse(image_texel_pointer); return SPV_SUCCESS; +#endif } opt::Instruction* GraphicsRobustAccessPass::InsertInst( diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 861ca2caa0..c4378d37bd 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -272,7 +272,7 @@ bool IRContext::ReplaceAllUsesWithPredicate( bool IRContext::IsConsistent() { #ifndef SPIRV_CHECK_CONTEXT return true; -#endif +#else if (AreAnalysesValid(kAnalysisDefUse)) { analysis::DefUseManager new_def_use(module()); if (*get_def_use_mgr() != new_def_use) { @@ -324,6 +324,7 @@ bool IRContext::IsConsistent() { } } return true; +#endif } void IRContext::ForgetUses(Instruction* inst) { From 4226b7d8a83dd0baff445c5316a11edabfcfab21 Mon Sep 17 00:00:00 2001 From: CHAITANYA Date: Fri, 17 Apr 2020 20:53:16 +0530 Subject: [PATCH 88/88] typo fix: in README.md exectuable->executable --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5714976d58..41c26a0a90 100644 --- a/README.md +++ b/README.md @@ -480,7 +480,7 @@ assembly and binary files with suffix `.spvasm` and `.spv`, respectively. The assembler reads the assembly language text, and emits the binary form. -The standalone assembler is the exectuable called `spirv-as`, and is located in +The standalone assembler is the executable called `spirv-as`, and is located in `/tools/spirv-as`. The functionality of the assembler is implemented by the `spvTextToBinary` library function.