From 3a02d1126872da4a21283551a31d88288180fe9e Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Thu, 24 Jun 2021 07:59:43 -0500 Subject: Add validation for SPV_EXT_shader_atomic_float16_add (#4325) --- source/val/validate_atomics.cpp | 7 +++++++ test/val/val_atomics_test.cpp | 23 ++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index fa53ca1f..da023b80 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp @@ -222,6 +222,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { if (opcode == SpvOpAtomicFAddEXT) { // result type being float checked already + if ((_.GetBitWidth(result_type) == 16) && + (!_.HasCapability(SpvCapabilityAtomicFloat16AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } if ((_.GetBitWidth(result_type) == 32) && (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index fc3aedbe..b7f6948e 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp @@ -318,7 +318,7 @@ TEST_F(ValidateAtomics, AtomicAddFloatVulkan) { EXPECT_THAT( getDiagnosticString(), HasSubstr("Opcode AtomicFAddEXT requires one of these capabilities: " - "AtomicFloat32AddEXT AtomicFloat64AddEXT")); + "AtomicFloat32AddEXT AtomicFloat64AddEXT AtomicFloat16AddEXT")); } TEST_F(ValidateAtomics, AtomicMinFloatVulkan) { @@ -539,6 +539,27 @@ OpExtension "SPV_EXT_shader_atomic_float_min_max" "require the AtomicFloat32MinMaxEXT capability")); } +TEST_F(ValidateAtomics, AtomicAddFloat16VulkanSuccess) { + const std::string defs = R"( +%f16 = OpTypeFloat 16 +%f16_1 = OpConstant %f16 1 +%f16_ptr = OpTypePointer Workgroup %f16 +%f16_var = OpVariable %f16_ptr Workgroup +)"; + const std::string body = R"( +%val1 = OpAtomicFAddEXT %f16 %f16_var %device %relaxed %f16_1 +)"; + const std::string extra = R"( +OpCapability Float16 +OpCapability AtomicFloat16AddEXT +OpExtension "SPV_EXT_shader_atomic_float16_add" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) { const std::string body = R"( %val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1 -- cgit v1.2.3 From 8cc8b6562be9de063a22ddf7f75d11c641779e36 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 24 Jun 2021 17:24:46 +0100 Subject: spirv-fuzz: Add illustrative tests for new issues (#4347) Adds (currently disabled) tests related to #4345 and #4346. --- ...transformation_replace_id_with_synonym_test.cpp | 235 +++++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 629a00ee..66c7ed09 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -1796,6 +1796,241 @@ TEST(TransformationReplaceIdWithSynonymTest, IncompatibleTypes) { .IsApplicable(context.get(), transformation_context)); } +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4346): This test +// should be updated to cover more atomic operations, and once the issue is +// fixed the test should be enabled. +TEST(TransformationReplaceIdWithSynonymTest, + DISABLED_AtomicScopeAndMemorySemanticsMustBeConstant) { + // The following SPIR-V came from this GLSL, edited to add some synonyms: + // + // #version 320 es + // + // #extension GL_KHR_memory_scope_semantics : enable + // + // layout(set = 0, binding = 0) buffer Buf { + // int x; + // }; + // + // void main() { + // int tmp = atomicLoad(x, + // gl_ScopeWorkgroup, + // gl_StorageSemanticsBuffer, + // gl_SemanticsRelaxed); + // } + const 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 320 + OpSourceExtension "GL_KHR_memory_scope_semantics" + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %9 + %11 = OpVariable %10 StorageBuffer + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 64 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 1 + %19 = OpConstant %17 0 + %20 = OpConstant %17 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpCopyObject %6 %15 ; A non-constant version of %15 + %101 = OpCopyObject %17 %20 ; A non-constant version of %20 + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %15 %20 + OpStore %8 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Tell the fact manager that %100 and %15 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {})); + + // Tell the fact manager that %101 and %20 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {})); + + const auto& scope_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand, 101) + .IsApplicable(context.get(), transformation_context)); +} + +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this +// test so that it covers more atomic operations, and enable the test once the +// issue is fixed. +TEST(TransformationReplaceIdWithSynonymTest, + DISABLED_SignOfAtomicScopeAndMemorySemanticsDoesNotMatter) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): both the + // GLSL comment and the corresponding SPIR-V should be updated to cover a + // larger number of atomic operations. + // The following SPIR-V came from this GLSL, edited to add some synonyms: + // + // #version 320 es + // + // #extension GL_KHR_memory_scope_semantics : enable + // + // layout(set = 0, binding = 0) buffer Buf { + // int x; + // }; + // + // void main() { + // int tmp = atomicLoad(x, + // gl_ScopeWorkgroup, + // gl_StorageSemanticsBuffer, + // gl_SemanticsRelaxed); + // } + const 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 320 + OpSourceExtension "GL_KHR_memory_scope_semantics" + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %9 + %11 = OpVariable %10 StorageBuffer + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 64 + %17 = OpTypeInt 32 0 + %100 = OpConstant %17 2 ; The same as %15, but with unsigned int type + %18 = OpConstant %17 1 + %19 = OpConstant %17 0 + %20 = OpConstant %17 64 + %101 = OpConstant %6 64 ; The same as %20, but with signed int type + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %15 %20 + OpStore %8 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Tell the fact manager that %100 and %15 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {})); + + // Tell the fact manager that %101 and %20 are synonymous + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {})); + + { + const auto& scope_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 1); + TransformationReplaceIdWithSynonym replace_scope(scope_operand, 100); + ASSERT_TRUE( + replace_scope.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_scope, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + const auto& semantics_operand = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(21), 2); + TransformationReplaceIdWithSynonym replace_semantics(semantics_operand, + 101); + ASSERT_TRUE( + replace_semantics.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_semantics, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpSourceExtension "GL_KHR_memory_scope_semantics" + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %9 + %11 = OpVariable %10 StorageBuffer + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 64 + %17 = OpTypeInt 32 0 + %100 = OpConstant %17 2 + %18 = OpConstant %17 1 + %19 = OpConstant %17 0 + %20 = OpConstant %17 64 + %101 = OpConstant %6 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %100 %101 + OpStore %8 %21 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools -- cgit v1.2.3 From 237173a07083a81199ffdaa8325037abbf09aebe Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 28 Jun 2021 17:53:49 +0100 Subject: spirv-reduce: Cleanup a few things (#4352) Cleans up a CMakeLists.txt file and the header guard for a reduction opportunity, and gets rid of an unnecessary parent function field that can be derived from an existing block field. --- source/reduce/CMakeLists.txt | 16 ++++++++-------- ...tructured_loop_to_selection_reduction_opportunity.cpp | 14 +++++++------- .../structured_loop_to_selection_reduction_opportunity.h | 14 +++++--------- ...ed_loop_to_selection_reduction_opportunity_finder.cpp | 4 ++-- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt index a3291c77..30432431 100644 --- a/source/reduce/CMakeLists.txt +++ b/source/reduce/CMakeLists.txt @@ -14,6 +14,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES change_operand_reduction_opportunity.h change_operand_to_undef_reduction_opportunity.h + conditional_branch_to_simple_conditional_branch_opportunity_finder.h + conditional_branch_to_simple_conditional_branch_reduction_opportunity.h merge_blocks_reduction_opportunity.h merge_blocks_reduction_opportunity_finder.h operand_to_const_reduction_opportunity_finder.h @@ -34,15 +36,15 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_struct_member_reduction_opportunity.h remove_unused_instruction_reduction_opportunity_finder.h remove_unused_struct_member_reduction_opportunity_finder.h - structured_loop_to_selection_reduction_opportunity.h - structured_loop_to_selection_reduction_opportunity_finder.h - conditional_branch_to_simple_conditional_branch_opportunity_finder.h - conditional_branch_to_simple_conditional_branch_reduction_opportunity.h simple_conditional_branch_to_branch_opportunity_finder.h simple_conditional_branch_to_branch_reduction_opportunity.h + structured_loop_to_selection_reduction_opportunity.h + structured_loop_to_selection_reduction_opportunity_finder.h change_operand_reduction_opportunity.cpp change_operand_to_undef_reduction_opportunity.cpp + conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp + conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp merge_blocks_reduction_opportunity.cpp merge_blocks_reduction_opportunity_finder.cpp operand_to_const_reduction_opportunity_finder.cpp @@ -63,12 +65,10 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_struct_member_reduction_opportunity.cpp remove_unused_instruction_reduction_opportunity_finder.cpp remove_unused_struct_member_reduction_opportunity_finder.cpp - structured_loop_to_selection_reduction_opportunity.cpp - structured_loop_to_selection_reduction_opportunity_finder.cpp - conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp - conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp simple_conditional_branch_to_branch_opportunity_finder.cpp simple_conditional_branch_to_branch_reduction_opportunity.cpp + structured_loop_to_selection_reduction_opportunity.cpp + structured_loop_to_selection_reduction_opportunity_finder.cpp ) if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp index 0c004439..022c978b 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -29,14 +29,14 @@ bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() { // Is the loop header reachable? return loop_construct_header_->GetLabel() ->context() - ->GetDominatorAnalysis(enclosing_function_) + ->GetDominatorAnalysis(loop_construct_header_->GetParent()) ->IsReachable(loop_construct_header_); } void StructuredLoopToSelectionReductionOpportunity::Apply() { // Force computation of dominator analysis, CFG and structured CFG analysis // before we start to mess with edges in the function. - context_->GetDominatorAnalysis(enclosing_function_); + context_->GetDominatorAnalysis(loop_construct_header_->GetParent()); context_->cfg(); context_->GetStructuredCFGAnalysis(); @@ -78,7 +78,7 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectToClosestMergeBlock( } already_seen.insert(pred); - if (!context_->GetDominatorAnalysis(enclosing_function_) + if (!context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) ->IsReachable(pred)) { // We do not care about unreachable predecessors (and dominance // information, and thus the notion of structured control flow, makes @@ -216,7 +216,7 @@ void StructuredLoopToSelectionReductionOpportunity::ChangeLoopToSelection() { void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { // Consider each instruction in the function. - for (auto& block : *enclosing_function_) { + for (auto& block : *loop_construct_header_->GetParent()) { for (auto& def : block) { if (def.opcode() == SpvOpVariable) { // Variables are defined at the start of the function, and can be @@ -243,7 +243,7 @@ void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { case SpvStorageClassFunction: use->SetOperand( index, {FindOrCreateFunctionVariable( - context_, enclosing_function_, + context_, loop_construct_header_->GetParent(), context_->get_type_mgr()->GetId(pointer_type))}); break; default: @@ -276,11 +276,11 @@ bool StructuredLoopToSelectionReductionOpportunity:: if (use->opcode() == SpvOpPhi) { // A use in a phi doesn't need to be dominated by its definition, but the // associated parent block does need to be dominated by the definition. - return context_->GetDominatorAnalysis(enclosing_function_) + return context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) ->Dominates(def_block.id(), use->GetSingleWordOperand(use_index + 1)); } // In non-phi cases, a use needs to be dominated by its definition. - return context_->GetDominatorAnalysis(enclosing_function_) + return context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) ->Dominates(def, use); } diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/source/reduce/structured_loop_to_selection_reduction_opportunity.h index 4c576196..0e3c840a 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity.h +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ -#define SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ +#ifndef SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ #include "source/opt/def_use_manager.h" #include "source/opt/dominator_analysis.h" @@ -30,11 +30,8 @@ class StructuredLoopToSelectionReductionOpportunity // Constructs an opportunity from a loop header block and the function that // encloses it. explicit StructuredLoopToSelectionReductionOpportunity( - opt::IRContext* context, opt::BasicBlock* loop_construct_header, - opt::Function* enclosing_function) - : context_(context), - loop_construct_header_(loop_construct_header), - enclosing_function_(enclosing_function) {} + opt::IRContext* context, opt::BasicBlock* loop_construct_header) + : context_(context), loop_construct_header_(loop_construct_header) {} // Returns true if the loop header is reachable. A structured loop might // become unreachable as a result of turning another structured loop into @@ -88,10 +85,9 @@ class StructuredLoopToSelectionReductionOpportunity opt::IRContext* context_; opt::BasicBlock* loop_construct_header_; - opt::Function* enclosing_function_; }; } // namespace reduce } // namespace spvtools -#endif // SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ +#endif // SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp index fdf3ab04..3fe61280 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp @@ -86,8 +86,8 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities( // We can turn this structured loop into a selection, so add the // opportunity to do so. result.push_back( - MakeUnique( - context, &block, function)); + MakeUnique(context, + &block)); } } return result; -- cgit v1.2.3 From 4fcdc5894676ef76707a14e20e50e4379ed18e89 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 28 Jun 2021 20:00:14 +0100 Subject: Add IsReachable function to IRContext (#4323) There was a lot of code in the codebase that would get the dominator analysis for a function and then use it to check whether a block is reachable. In the fuzzer, a utility method had been introduced to make this more concise, but it was not being used consistently. This change moves the utility method to IRContext, so that it can be used throughout the codebase, and refactors all existing checks for block reachability to use the utility method. --- source/fuzz/available_instructions.cpp | 2 +- source/fuzz/fuzzer_pass.cpp | 4 +-- .../fuzzer_pass_propagate_instructions_down.cpp | 3 +-- .../fuzzer_pass_push_ids_through_variables.cpp | 2 +- source/fuzz/fuzzer_util.cpp | 29 ++++++++-------------- source/fuzz/fuzzer_util.h | 5 ---- source/fuzz/transformation_add_dead_block.cpp | 6 ++--- source/fuzz/transformation_add_dead_break.cpp | 2 +- source/fuzz/transformation_add_dead_continue.cpp | 3 +-- .../transformation_flatten_conditional_branch.cpp | 10 ++++---- source/fuzz/transformation_outline_function.cpp | 3 +-- .../transformation_propagate_instruction_down.cpp | 10 ++++---- .../transformation_push_id_through_variable.cpp | 2 +- source/opt/block_merge_util.cpp | 4 +-- source/opt/ir_context.cpp | 6 +++++ source/opt/ir_context.h | 6 ++++- ...red_loop_to_selection_reduction_opportunity.cpp | 9 +++---- 17 files changed, 46 insertions(+), 60 deletions(-) diff --git a/source/fuzz/available_instructions.cpp b/source/fuzz/available_instructions.cpp index e25ed900..0db8b207 100644 --- a/source/fuzz/available_instructions.cpp +++ b/source/fuzz/available_instructions.cpp @@ -44,7 +44,7 @@ AvailableInstructions::AvailableInstructions( // Consider every reachable block in the function. auto dominator_analysis = ir_context->GetDominatorAnalysis(&function); for (auto& block : function) { - if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, &block)) { + if (!ir_context->IsReachable(block)) { // The block is not reachable. continue; } diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index f67efc66..9e95190c 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -111,10 +111,8 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor( // module. std::vector reachable_blocks; - const auto* dominator_analysis = - GetIRContext()->GetDominatorAnalysis(function); for (auto& block : *function) { - if (dominator_analysis->IsReachable(&block)) { + if (GetIRContext()->IsReachable(block)) { reachable_blocks.push_back(&block); } } diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp index af27a5da..89e14370 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp +++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp @@ -31,8 +31,7 @@ void FuzzerPassPropagateInstructionsDown::Apply() { for (const auto& function : *GetIRContext()->module()) { std::vector reachable_blocks; for (const auto& block : function) { - if (GetIRContext()->GetDominatorAnalysis(&function)->IsReachable( - &block)) { + if (GetIRContext()->IsReachable(block)) { reachable_blocks.push_back(&block); } } diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 54e589c9..7b436c17 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -47,7 +47,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() { // The block containing the instruction we are going to insert before // must be reachable. - if (!fuzzerutil::BlockIsReachableInItsFunction(GetIRContext(), block)) { + if (!GetIRContext()->IsReachable(*block)) { return; } diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 08b927e0..4beb67fd 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -252,11 +252,11 @@ bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, return false; } - // |block_id| must be reachable and be dominated by |loop_header|. + // |block| must be reachable and be dominated by |loop_header|. opt::DominatorAnalysis* dominator_analysis = context->GetDominatorAnalysis(loop_header->GetParent()); - return dominator_analysis->IsReachable(block_id) && - dominator_analysis->Dominates(loop_header_id, block_id); + return context->IsReachable(*block) && + dominator_analysis->Dominates(loop_header, block); } bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, @@ -284,13 +284,6 @@ opt::BasicBlock::iterator GetIteratorForInstruction( return block->end(); } -bool BlockIsReachableInItsFunction(opt::IRContext* context, - opt::BasicBlock* bb) { - auto enclosing_function = bb->GetParent(); - return context->GetDominatorAnalysis(enclosing_function) - ->Dominates(enclosing_function->entry().get(), bb); -} - bool CanInsertOpcodeBeforeInstruction( SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) { if (instruction_in_block->PreviousNode() && @@ -660,13 +653,12 @@ bool IdIsAvailableAtUse(opt::IRContext* context, // It is not OK for a definition to use itself. return false; } - auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); - if (!dominator_analysis->IsReachable( - context->get_instr_block(use_instruction)) || - !dominator_analysis->IsReachable(context->get_instr_block(id))) { + if (!context->IsReachable(*context->get_instr_block(use_instruction)) || + !context->IsReachable(*context->get_instr_block(id))) { // Skip unreachable blocks. 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 @@ -704,8 +696,8 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context, } const auto* dominator_analysis = context->GetDominatorAnalysis(function_enclosing_instruction); - if (dominator_analysis->IsReachable(context->get_instr_block(instruction)) && - dominator_analysis->IsReachable(context->get_instr_block(id)) && + if (context->IsReachable(*context->get_instr_block(instruction)) && + context->IsReachable(*context->get_instr_block(id)) && dominator_analysis->Dominates(id_definition, instruction)) { // The id's definition dominates the instruction, and both the definition // and the instruction are in reachable blocks, thus the id is available at @@ -715,8 +707,7 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context, if (id_definition->opcode() == SpvOpVariable && function_enclosing_instruction == context->get_instr_block(id)->GetParent()) { - assert(!dominator_analysis->IsReachable( - context->get_instr_block(instruction)) && + assert(!context->IsReachable(*context->get_instr_block(instruction)) && "If the instruction were in a reachable block we should already " "have returned true."); // The id is a variable and it is in the same function as |instruction|. @@ -1883,7 +1874,7 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, // all its dependencies satisfy domination rules (i.e. all id operands // dominate that instruction). for (const auto& block : *mutated_block->GetParent()) { - if (!dominator_analysis.IsReachable(&block)) { + if (!ir_context->IsReachable(block)) { // If some block is not reachable then we don't need to worry about the // preservation of domination rules for its instructions. continue; diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index dd7bd961..b956507a 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -108,11 +108,6 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, opt::BasicBlock::iterator GetIteratorForInstruction( opt::BasicBlock* block, const opt::Instruction* inst); -// Returns true if and only if there is a path to |bb| from the entry block of -// the function that contains |bb|. -bool BlockIsReachableInItsFunction(opt::IRContext* context, - opt::BasicBlock* bb); - // Determines whether it is OK to insert an instruction with opcode |opcode| // before |instruction_in_block|. bool CanInsertOpcodeBeforeInstruction( diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp index 82e8cd8f..df700ce5 100644 --- a/source/fuzz/transformation_add_dead_block.cpp +++ b/source/fuzz/transformation_add_dead_block.cpp @@ -79,9 +79,7 @@ bool TransformationAddDeadBlock::IsApplicable( } // |existing_block| must be reachable. - opt::DominatorAnalysis* dominator_analysis = - ir_context->GetDominatorAnalysis(existing_block->GetParent()); - if (!dominator_analysis->IsReachable(existing_block->id())) { + if (!ir_context->IsReachable(*existing_block)) { return false; } @@ -94,6 +92,8 @@ bool TransformationAddDeadBlock::IsApplicable( // the selection construct, its header |existing_block| will not dominate the // merge block |successor_block_id|, which is invalid. Thus, |existing_block| // must dominate |successor_block_id|. + opt::DominatorAnalysis* dominator_analysis = + ir_context->GetDominatorAnalysis(existing_block->GetParent()); if (!dominator_analysis->Dominates(existing_block->id(), successor_block_id)) { return false; diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp index ad46ce7f..32080ca4 100644 --- a/source/fuzz/transformation_add_dead_break.cpp +++ b/source/fuzz/transformation_add_dead_break.cpp @@ -134,7 +134,7 @@ bool TransformationAddDeadBreak::IsApplicable( return false; } - if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) { + if (!ir_context->IsReachable(*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. diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp index be6294e8..f2b9ab3f 100644 --- a/source/fuzz/transformation_add_dead_continue.cpp +++ b/source/fuzz/transformation_add_dead_continue.cpp @@ -83,8 +83,7 @@ bool TransformationAddDeadContinue::IsApplicable( auto continue_block = ir_context->cfg()->block(loop_header)->ContinueBlockId(); - if (!fuzzerutil::BlockIsReachableInItsFunction( - ir_context, ir_context->cfg()->block(continue_block))) { + if (!ir_context->IsReachable(*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. diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp index b8c6de0c..127e7628 100644 --- a/source/fuzz/transformation_flatten_conditional_branch.cpp +++ b/source/fuzz/transformation_flatten_conditional_branch.cpp @@ -441,17 +441,17 @@ bool TransformationFlattenConditionalBranch:: header->terminator()->opcode() == SpvOpBranchConditional && "|header| must be the header of a conditional."); + // |header| must be reachable. + if (!ir_context->IsReachable(*header)) { + return false; + } + auto enclosing_function = header->GetParent(); auto dominator_analysis = ir_context->GetDominatorAnalysis(enclosing_function); auto postdominator_analysis = ir_context->GetPostDominatorAnalysis(enclosing_function); - // |header| must be reachable. - if (!dominator_analysis->IsReachable(header)) { - return false; - } - // Check that the header and the merge block describe a single-entry, // single-exit region. if (!dominator_analysis->Dominates(header->id(), merge_block_id) || diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 84e8ac2c..3140fa6b 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -180,8 +180,7 @@ bool TransformationOutlineFunction::IsApplicable( // predecessors. If it does, then we do not regard the region as single- // entry-single-exit and hence do not outline it. for (auto pred : ir_context->cfg()->preds(block.id())) { - if (!fuzzerutil::BlockIsReachableInItsFunction( - ir_context, ir_context->cfg()->block(pred))) { + if (!ir_context->IsReachable(*ir_context->cfg()->block(pred))) { // The predecessor is unreachable. return false; } diff --git a/source/fuzz/transformation_propagate_instruction_down.cpp b/source/fuzz/transformation_propagate_instruction_down.cpp index 7713562e..c3b7c4d9 100644 --- a/source/fuzz/transformation_propagate_instruction_down.cpp +++ b/source/fuzz/transformation_propagate_instruction_down.cpp @@ -386,11 +386,8 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock( return false; } - const auto* dominator_analysis = - ir_context->GetDominatorAnalysis(block->GetParent()); - // |block| must be reachable. - if (!dominator_analysis->IsReachable(block)) { + if (!ir_context->IsReachable(*block)) { return false; } @@ -430,6 +427,9 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock( auto phi_block_id = GetOpPhiBlockId(ir_context, block_id, *inst_to_propagate, successor_ids); + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(block->GetParent()); + // Make sure we can adjust all users of the propagated instruction. return ir_context->get_def_use_mgr()->WhileEachUse( inst_to_propagate, @@ -537,7 +537,7 @@ uint32_t TransformationPropagateInstructionDown::GetOpPhiBlockId( // Check that |merge_block_id| is reachable in the CFG and |block_id| // dominates |merge_block_id|. - if (!dominator_analysis->IsReachable(merge_block_id) || + if (!ir_context->IsReachable(*ir_context->cfg()->block(merge_block_id)) || !dominator_analysis->Dominates(block_id, merge_block_id)) { return 0; } diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp index 0df1da6b..ff525165 100644 --- a/source/fuzz/transformation_push_id_through_variable.cpp +++ b/source/fuzz/transformation_push_id_through_variable.cpp @@ -61,7 +61,7 @@ bool TransformationPushIdThroughVariable::IsApplicable( // The instruction to insert before must belong to a reachable block. auto basic_block = ir_context->get_instr_block(instruction_to_insert_before); - if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, basic_block)) { + if (!ir_context->IsReachable(*basic_block)) { return false; } diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp index 14b5d364..39b50742 100644 --- a/source/opt/block_merge_util.cpp +++ b/source/opt/block_merge_util.cpp @@ -104,9 +104,7 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { } // Don't bother trying to merge unreachable blocks. - if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) { - if (!dominators->IsReachable(block)) return false; - } + if (!context->IsReachable(*block)) return false; Instruction* merge_inst = block->GetMergeInst(); const bool pred_is_header = IsHeader(block); diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 03afe6e8..74f38786 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -1034,5 +1034,11 @@ bool IRContext::CheckCFG() { return true; } + +bool IRContext::IsReachable(const opt::BasicBlock& bb) { + auto enclosing_function = bb.GetParent(); + return GetDominatorAnalysis(enclosing_function) + ->Dominates(enclosing_function->entry().get(), &bb); +} } // namespace opt } // namespace spvtools diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index aab35162..f5f38a64 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -598,10 +598,14 @@ class IRContext { bool ProcessCallTreeFromRoots(ProcessFunction& pfn, std::queue* roots); - // Emmits a error message to the message consumer indicating the error + // Emits a error message to the message consumer indicating the error // described by |message| occurred in |inst|. void EmitErrorMessage(std::string message, Instruction* inst); + // Returns true if and only if there is a path to |bb| from the entry block of + // the function that contains |bb|. + bool IsReachable(const opt::BasicBlock& bb); + private: // Builds the def-use manager from scratch, even if it was already valid. void BuildDefUseManager() { diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp index 022c978b..850af456 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -27,10 +27,8 @@ const uint32_t kMergeNodeIndex = 0; bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() { // Is the loop header reachable? - return loop_construct_header_->GetLabel() - ->context() - ->GetDominatorAnalysis(loop_construct_header_->GetParent()) - ->IsReachable(loop_construct_header_); + return loop_construct_header_->GetLabel()->context()->IsReachable( + *loop_construct_header_); } void StructuredLoopToSelectionReductionOpportunity::Apply() { @@ -78,8 +76,7 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectToClosestMergeBlock( } already_seen.insert(pred); - if (!context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) - ->IsReachable(pred)) { + if (!context_->IsReachable(*context_->cfg()->block(pred))) { // We do not care about unreachable predecessors (and dominance // information, and thus the notion of structured control flow, makes // little sense for unreachable blocks). -- cgit v1.2.3 From b8587c984a935cfdced6b523de3378eafb31659f Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 28 Jun 2021 23:05:30 +0100 Subject: spirv-reduce: Allow merging unreachable blocks (#4303) This change allows the reducer to merge together blocks even when they are unreachable, but keeps the restriction of reachability in place for the optimizer. Fixes #4302. --- source/fuzz/transformation_merge_blocks.cpp | 3 ++ source/fuzz/transformation_merge_blocks.h | 1 + source/opt/block_merge_pass.cpp | 4 +- source/opt/block_merge_util.cpp | 3 -- test/reduce/merge_blocks_test.cpp | 58 +++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 4 deletions(-) diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp index 22236795..dbf782e2 100644 --- a/source/fuzz/transformation_merge_blocks.cpp +++ b/source/fuzz/transformation_merge_blocks.cpp @@ -43,6 +43,9 @@ bool TransformationMergeBlocks::IsApplicable( } auto first_block = ir_context->cfg()->block(predecessors.at(0)); + if (!ir_context->IsReachable(*first_block)) { + return false; + } return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block); } diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h index f6306c5a..f3462fae 100644 --- a/source/fuzz/transformation_merge_blocks.h +++ b/source/fuzz/transformation_merge_blocks.h @@ -31,6 +31,7 @@ class TransformationMergeBlocks : public Transformation { TransformationMergeBlocks(uint32_t block_id); // - |message_.block_id| must be the id of a block, b + // - b must be statically reachable in the control flow graph of its function // - b must have a single predecessor, a // - b must be the sole successor of a // - Replacing a with the merge of a and b (and removing b) must lead to a diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp index c7315baf..04e47f1c 100644 --- a/source/opt/block_merge_pass.cpp +++ b/source/opt/block_merge_pass.cpp @@ -28,7 +28,9 @@ namespace opt { bool BlockMergePass::MergeBlocks(Function* func) { bool modified = false; for (auto bi = func->begin(); bi != func->end();) { - if (blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) { + // Don't bother trying to merge unreachable blocks. + if (context()->IsReachable(*bi) && + blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) { blockmergeutil::MergeWithSuccessor(context(), func, bi); // Reprocess block. modified = true; diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp index 39b50742..15e8c6ff 100644 --- a/source/opt/block_merge_util.cpp +++ b/source/opt/block_merge_util.cpp @@ -103,9 +103,6 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { return false; } - // Don't bother trying to merge unreachable blocks. - if (!context->IsReachable(*block)) return false; - Instruction* merge_inst = block->GetMergeInst(); const bool pred_is_header = IsHeader(block); if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) { diff --git a/test/reduce/merge_blocks_test.cpp b/test/reduce/merge_blocks_test.cpp index 8506ee08..c472301f 100644 --- a/test/reduce/merge_blocks_test.cpp +++ b/test/reduce/merge_blocks_test.cpp @@ -647,6 +647,64 @@ TEST(MergeBlocksReductionPassTest, LoopReturnReverse) { MergeBlocksReductionPassTest_LoopReturn_Helper(true); } +TEST(MergeBlocksReductionPassTest, MergeUnreachable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %11 = OpTypeBool + %12 = OpConstantFalse %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + %9 = OpLabel + OpBranch %100 + %100 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %11 = OpTypeBool + %12 = OpConstantFalse %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + } // namespace } // namespace reduce } // namespace spvtools -- cgit v1.2.3 From eeff9af1e6e178de60b9e3adf7e0f2f0d655473f Mon Sep 17 00:00:00 2001 From: John Zupin Date: Tue, 29 Jun 2021 08:07:13 -0600 Subject: fix strncpy bound error (#4331) --- source/diagnostic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/diagnostic.cpp b/source/diagnostic.cpp index edc27c8f..f3aa2594 100644 --- a/source/diagnostic.cpp +++ b/source/diagnostic.cpp @@ -37,7 +37,7 @@ spv_diagnostic spvDiagnosticCreate(const spv_position position, diagnostic->position = *position; diagnostic->isTextSource = false; memset(diagnostic->error, 0, length); - strncpy(diagnostic->error, message, length); + strcpy(diagnostic->error, message); return diagnostic; } -- cgit v1.2.3 From 8442a181294b42d05e365bf3e4e1df13ed69cf99 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 29 Jun 2021 11:25:32 -0400 Subject: Bump glob-parent from 5.0.0 to 5.1.2. (#4353) --- 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 4924690a..afa11f6c 100644 --- a/tools/sva/yarn.lock +++ b/tools/sva/yarn.lock @@ -694,9 +694,9 @@ get-stream@^4.0.0: pump "^3.0.0" glob-parent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" - integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -- cgit v1.2.3 From f9893c4549406eb9643e0eb05a521ab70a320fff Mon Sep 17 00:00:00 2001 From: ZHOU He <3.1416f@gmail.com> Date: Tue, 29 Jun 2021 23:33:58 +0800 Subject: spirv-opt: A pass to removed unused input on OpEntryPoint instructions. (#4275) The new pass will removed interface variable on the OpEntryPoint instruction when they are not statically referenced in the call tree of the entry point. It can be enabled on the command line using the options `remove-unused-interface-variables`. --- Android.mk | 1 + include/spirv-tools/optimizer.hpp | 7 + source/opt/CMakeLists.txt | 2 + source/opt/optimizer.cpp | 7 + source/opt/passes.h | 1 + .../opt/remove_unused_interface_variables_pass.cpp | 93 +++++++++++ .../opt/remove_unused_interface_variables_pass.h | 26 +++ test/opt/CMakeLists.txt | 1 + .../opt/remove_unused_interface_variables_test.cpp | 185 +++++++++++++++++++++ tools/opt/opt.cpp | 6 + utils/check_copyright.py | 3 +- 11 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 source/opt/remove_unused_interface_variables_pass.cpp create mode 100644 source/opt/remove_unused_interface_variables_pass.h create mode 100644 test/opt/remove_unused_interface_variables_test.cpp diff --git a/Android.mk b/Android.mk index ef1cdff1..1bc844d4 100644 --- a/Android.mk +++ b/Android.mk @@ -153,6 +153,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/register_pressure.cpp \ source/opt/relax_float_ops_pass.cpp \ source/opt/remove_duplicates_pass.cpp \ + source/opt/remove_unused_interface_variables_pass.cpp \ source/opt/replace_invalid_opc.cpp \ source/opt/scalar_analysis.cpp \ source/opt/scalar_analysis_simplification.cpp \ diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index e8b5b698..700f59f7 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -514,6 +514,13 @@ Optimizer::PassToken CreateDeadInsertElimPass(); // eliminated with standard dead code elimination. Optimizer::PassToken CreateAggressiveDCEPass(); +// Creates a remove-unused-interface-variables pass. +// Removes variables referenced on the |OpEntryPoint| instruction that are not +// referenced in the entry point function or any function in its call tree. Note +// that this could cause the shader interface to no longer match other shader +// stages. +Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass(); + // Creates an empty pass. // This is deprecated and will be removed. // TODO(jaebaek): remove this pass after handling glslang's broken unit tests. diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 88d56589..0e41b205 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -96,6 +96,7 @@ set(SPIRV_TOOLS_OPT_SOURCES register_pressure.h relax_float_ops_pass.h remove_duplicates_pass.h + remove_unused_interface_variables_pass.h replace_invalid_opc.h scalar_analysis.h scalar_analysis_nodes.h @@ -197,6 +198,7 @@ set(SPIRV_TOOLS_OPT_SOURCES register_pressure.cpp relax_float_ops_pass.cpp remove_duplicates_pass.cpp + remove_unused_interface_variables_pass.cpp replace_invalid_opc.cpp scalar_analysis.cpp scalar_analysis_simplification.cpp diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index a5d10c3d..69103b26 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -489,6 +489,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterSizePasses(); } else if (pass_name == "legalize-hlsl") { RegisterLegalizationPasses(); + } else if (pass_name == "remove-unused-interface-variables") { + RegisterPass(CreateRemoveUnusedInterfaceVariablesPass()); } else if (pass_name == "graphics-robust-access") { RegisterPass(CreateGraphicsRobustAccessPass()); } else if (pass_name == "wrap-opkill") { @@ -729,6 +731,11 @@ Optimizer::PassToken CreateAggressiveDCEPass() { MakeUnique()); } +Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() { + return MakeUnique( + MakeUnique()); +} + Optimizer::PassToken CreatePropagateLineInfoPass() { return MakeUnique(MakeUnique()); } diff --git a/source/opt/passes.h b/source/opt/passes.h index bfb34af7..aeae4449 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -64,6 +64,7 @@ #include "source/opt/redundancy_elimination.h" #include "source/opt/relax_float_ops_pass.h" #include "source/opt/remove_duplicates_pass.h" +#include "source/opt/remove_unused_interface_variables_pass.h" #include "source/opt/replace_invalid_opc.h" #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" diff --git a/source/opt/remove_unused_interface_variables_pass.cpp b/source/opt/remove_unused_interface_variables_pass.cpp new file mode 100644 index 00000000..31e87bd4 --- /dev/null +++ b/source/opt/remove_unused_interface_variables_pass.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 ZHOU He +// +// 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 "remove_unused_interface_variables_pass.h" +#include "source/spirv_constant.h" +namespace spvtools { +namespace opt { + +class RemoveUnusedInterfaceVariablesContext { + RemoveUnusedInterfaceVariablesPass& parent_; + Instruction& entry_; + std::unordered_set used_variables_; + IRContext::ProcessFunction pfn_ = + std::bind(&RemoveUnusedInterfaceVariablesContext::processFunction, this, + std::placeholders::_1); + + bool processFunction(Function* func) { + for (const auto& basic_block : *func) + for (const auto& instruction : basic_block) + instruction.ForEachInId([&](const uint32_t* id) { + if (used_variables_.count(*id)) return; + auto* var = parent_.get_def_use_mgr()->GetDef(*id); + if (!var || var->opcode() != SpvOpVariable) return; + auto storage_class = var->GetSingleWordInOperand(0); + if (storage_class != SpvStorageClassFunction && + (parent_.get_module()->version() >= + SPV_SPIRV_VERSION_WORD(1, 4) || + storage_class == SpvStorageClassInput || + storage_class == SpvStorageClassOutput)) + used_variables_.insert(*id); + }); + return false; + } + + public: + RemoveUnusedInterfaceVariablesContext( + RemoveUnusedInterfaceVariablesPass& parent, Instruction& entry) + : parent_(parent), entry_(entry) {} + + void CollectUsedVariables() { + std::queue roots; + roots.push(entry_.GetSingleWordInOperand(1)); + parent_.context()->ProcessCallTreeFromRoots(pfn_, &roots); + } + + bool ShouldModify() { + std::unordered_set old_variables; + for (int i = entry_.NumInOperands() - 1; i >= 3; --i) { + auto variable = entry_.GetInOperand(i).words[0]; + if (!used_variables_.count(variable)) return true; // It is unused. + if (old_variables.count(variable)) return true; // It is duplicate. + old_variables.insert(variable); + } + if (old_variables.size() != used_variables_.size()) // Missing IDs. + return true; + return false; + } + + void Modify() { + for (int i = entry_.NumInOperands() - 1; i >= 3; --i) + entry_.RemoveInOperand(i); + for (auto id : used_variables_) { + entry_.AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id})); + } + } +}; + +RemoveUnusedInterfaceVariablesPass::Status +RemoveUnusedInterfaceVariablesPass::Process() { + bool modified = false; + for (auto& entry : get_module()->entry_points()) { + RemoveUnusedInterfaceVariablesContext context(*this, entry); + context.CollectUsedVariables(); + if (context.ShouldModify()) { + context.Modify(); + modified = true; + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} +} // namespace opt +} // namespace spvtools \ No newline at end of file diff --git a/source/opt/remove_unused_interface_variables_pass.h b/source/opt/remove_unused_interface_variables_pass.h new file mode 100644 index 00000000..7f11187c --- /dev/null +++ b/source/opt/remove_unused_interface_variables_pass.h @@ -0,0 +1,26 @@ +// Copyright (c) 2021 ZHOU He +// +// 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/opt/pass.h" +namespace spvtools { +namespace opt { + +class RemoveUnusedInterfaceVariablesPass : public Pass { + const char* name() const override { + return "remove-unused-interface-variables-pass"; + } + Status Process() override; +}; +} // namespace opt +} // namespace spvtools \ No newline at end of file diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index f65d2ff3..621a6aa4 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -79,6 +79,7 @@ add_spvtools_unittest(TARGET opt propagator_test.cpp reduce_load_size_test.cpp redundancy_elimination_test.cpp + remove_unused_interface_variables_test.cpp register_liveness.cpp relax_float_ops_test.cpp replace_invalid_opc_test.cpp diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp new file mode 100644 index 00000000..ddf027f1 --- /dev/null +++ b/test/opt/remove_unused_interface_variables_test.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 ZHOU He +// +// 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 "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using RemoveUnusedInterfaceVariablesTest = PassTest<::testing::Test>; + +static const std::string expected = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %_Z5func1v "_Z5func1v" %out_var_SV_TARGET +OpEntryPoint Fragment %_Z5func2v "_Z5func2v" %out_var_SV_TARGET_0 +OpExecutionMode %_Z5func1v OriginUpperLeft +OpExecutionMode %_Z5func2v OriginUpperLeft +OpSource HLSL 630 +OpName %type_cba "type.cba" +OpMemberName %type_cba 0 "color" +OpName %cba "cba" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET" +OpName %_Z5func1v "_Z5func1v" +OpName %_Z5func2v "_Z5func2v" +OpDecorate %out_var_SV_TARGET Location 0 +OpDecorate %out_var_SV_TARGET_0 Location 0 +OpDecorate %cba DescriptorSet 0 +OpDecorate %cba Binding 0 +OpMemberDecorate %type_cba 0 Offset 0 +OpDecorate %type_cba Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%type_cba = OpTypeStruct %v4float +%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%14 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%cba = OpVariable %_ptr_Uniform_type_cba Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output +%_Z5func1v = OpFunction %void None %14 +%16 = OpLabel +%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%18 = OpLoad %v4float %17 +OpStore %out_var_SV_TARGET %18 +OpReturn +OpFunctionEnd +%_Z5func2v = OpFunction %void None %14 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%21 = OpLoad %v4float %20 +OpStore %out_var_SV_TARGET_0 %21 +OpReturn +OpFunctionEnd +)"; + +TEST_F(RemoveUnusedInterfaceVariablesTest, RemoveUnusedVariable) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %_Z5func1v "_Z5func1v" %out_var_SV_TARGET %out_var_SV_TARGET_0 +OpEntryPoint Fragment %_Z5func2v "_Z5func2v" %out_var_SV_TARGET %out_var_SV_TARGET_0 +OpExecutionMode %_Z5func1v OriginUpperLeft +OpExecutionMode %_Z5func2v OriginUpperLeft +OpSource HLSL 630 +OpName %type_cba "type.cba" +OpMemberName %type_cba 0 "color" +OpName %cba "cba" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET" +OpName %_Z5func1v "_Z5func1v" +OpName %_Z5func2v "_Z5func2v" +OpDecorate %out_var_SV_TARGET Location 0 +OpDecorate %out_var_SV_TARGET_0 Location 0 +OpDecorate %cba DescriptorSet 0 +OpDecorate %cba Binding 0 +OpMemberDecorate %type_cba 0 Offset 0 +OpDecorate %type_cba Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%type_cba = OpTypeStruct %v4float +%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%14 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%cba = OpVariable %_ptr_Uniform_type_cba Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output +%_Z5func1v = OpFunction %void None %14 +%16 = OpLabel +%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%18 = OpLoad %v4float %17 +OpStore %out_var_SV_TARGET %18 +OpReturn +OpFunctionEnd +%_Z5func2v = OpFunction %void None %14 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%21 = OpLoad %v4float %20 +OpStore %out_var_SV_TARGET_0 %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, expected, + true, true); +} + +TEST_F(RemoveUnusedInterfaceVariablesTest, FixMissingVariable) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %_Z5func1v "_Z5func1v" +OpEntryPoint Fragment %_Z5func2v "_Z5func2v" +OpExecutionMode %_Z5func1v OriginUpperLeft +OpExecutionMode %_Z5func2v OriginUpperLeft +OpSource HLSL 630 +OpName %type_cba "type.cba" +OpMemberName %type_cba 0 "color" +OpName %cba "cba" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET" +OpName %_Z5func1v "_Z5func1v" +OpName %_Z5func2v "_Z5func2v" +OpDecorate %out_var_SV_TARGET Location 0 +OpDecorate %out_var_SV_TARGET_0 Location 0 +OpDecorate %cba DescriptorSet 0 +OpDecorate %cba Binding 0 +OpMemberDecorate %type_cba 0 Offset 0 +OpDecorate %type_cba Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%type_cba = OpTypeStruct %v4float +%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%14 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%cba = OpVariable %_ptr_Uniform_type_cba Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output +%_Z5func1v = OpFunction %void None %14 +%16 = OpLabel +%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%18 = OpLoad %v4float %17 +OpStore %out_var_SV_TARGET %18 +OpReturn +OpFunctionEnd +%_Z5func2v = OpFunction %void None %14 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0 +%21 = OpLoad %v4float %20 +OpStore %out_var_SV_TARGET_0 %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, expected, + true, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index b9339abe..53bf31d6 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -406,6 +406,12 @@ Options (in lexicographical order):)", Removes duplicate types, decorations, capabilities and extension instructions.)"); printf(R"( + --remove-unused-interface-variables + Removes variables referenced on the |OpEntryPoint| instruction + that are not referenced in the entry point function or any function + in its call tree. Note that this could cause the shader interface + to no longer match other shader stages.)"); + printf(R"( --replace-invalid-opcode Replaces instructions whose opcode is valid for shader modules, but not for the current shader stage. To have an effect, all diff --git a/utils/check_copyright.py b/utils/check_copyright.py index c5251230..49892ee6 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -39,7 +39,8 @@ AUTHORS = ['The Khronos Group Inc.', 'Stefano Milizia', 'Alastair F. Donaldson', 'Mostafa Ashraf', - 'Shiyu Liu'] + 'Shiyu Liu', + 'ZHOU He'] CURRENT_YEAR='2021' YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)' -- cgit v1.2.3 From 74e8105eb8010d0c31e3ec1a477770efeda89478 Mon Sep 17 00:00:00 2001 From: Assiduous <61806567+TheMostDiligent@users.noreply.github.com> Date: Tue, 29 Jun 2021 12:14:38 -0700 Subject: Enabled tvOS platform (#4329) --- CMakeLists.txt | 2 ++ source/print.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55f84e6d..35689491 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,8 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") add_definitions(-DSPIRV_MAC) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS") add_definitions(-DSPIRV_IOS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "tvOS") + add_definitions(-DSPIRV_TVOS) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") add_definitions(-DSPIRV_ANDROID) set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) diff --git a/source/print.cpp b/source/print.cpp index 128587ae..2418c5bc 100644 --- a/source/print.cpp +++ b/source/print.cpp @@ -15,7 +15,7 @@ #include "source/print.h" #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \ - defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || \ + defined(SPIRV_IOS) || defined(SPIRV_TVOS) || defined(SPIRV_FREEBSD) || \ defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA) namespace spvtools { -- cgit v1.2.3 From 06f114d482143bd3c2fae6c8d8174a39521b09ce Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 1 Jul 2021 14:51:13 +0000 Subject: spirv-fuzz: Avoid out of bounds access (#4355) In the extreme case where there are no basic types, FuzzerPassPushIdsThroughVariables could trigger a bounds error. This change fixes this problem. --- source/fuzz/fuzzer_pass_push_ids_through_variables.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 7b436c17..6aebac66 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -69,6 +69,12 @@ void FuzzerPassPushIdsThroughVariables::Apply() { auto basic_type_ids_and_pointers = GetAvailableBasicTypesAndPointers(variable_storage_class); auto& basic_types = basic_type_ids_and_pointers.first; + + // There must be at least some basic types. + if (basic_types.empty()) { + return; + } + uint32_t basic_type_id = basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; -- cgit v1.2.3 From c67f132087b92aa3e5e869dd300400669409c797 Mon Sep 17 00:00:00 2001 From: Ben Ashbaugh Date: Fri, 2 Jul 2021 05:53:18 -0700 Subject: add tests for SPV_KHR_bit_instructions (#4350) --- source/val/validate_extensions.cpp | 1 + test/text_to_binary.extension_test.cpp | 15 +++ test/val/CMakeLists.txt | 1 + .../val/val_extension_spv_khr_bit_instructions.cpp | 117 +++++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 test/val/val_extension_spv_khr_bit_instructions.cpp diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index a7167fc1..b5b98783 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -3112,6 +3112,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { case OpenCLDebugInfo100DebugMacroDef: case OpenCLDebugInfo100DebugMacroUndef: case OpenCLDebugInfo100DebugImportedEntity: + case OpenCLDebugInfo100DebugModuleINTEL: break; case OpenCLDebugInfo100InstructionsMax: assert(0); diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 08579843..1324206c 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -1018,5 +1018,20 @@ INSTANTIATE_TEST_SUITE_P( SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, }))); +// SPV_KHR_bit_instructions + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_bit_instructions, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + ValuesIn(std::vector{ + {"OpExtension \"SPV_KHR_bit_instructions\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_bit_instructions"))}, + {"OpCapability BitInstructions\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityBitInstructions})}, + }))); + } // namespace } // namespace spvtools diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 83249641..b1e12ac0 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -42,6 +42,7 @@ add_spvtools_unittest(TARGET val_abcde val_extension_spv_khr_linkonce_odr.cpp val_extension_spv_khr_subgroup_uniform_control_flow.cpp val_extension_spv_khr_integer_dot_product.cpp + val_extension_spv_khr_bit_instructions.cpp val_extension_spv_khr_terminate_invocation.cpp val_ext_inst_test.cpp ${VAL_TEST_COMMON_SRCS} diff --git a/test/val/val_extension_spv_khr_bit_instructions.cpp b/test/val/val_extension_spv_khr_bit_instructions.cpp new file mode 100644 index 00000000..0e926716 --- /dev/null +++ b/test/val/val_extension_spv_khr_bit_instructions.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2021 The Khronos Group 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. + +// Tests for OpExtension validator rules. + +#include +#include + +#include "gmock/gmock.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateSpvKHRBitInstructions = spvtest::ValidateBase; + +TEST_F(ValidateSpvKHRBitInstructions, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability BitInstructions + OpExtension "SPV_KHR_bit_instructions" + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_1 = OpConstant %u32 1 + + %main = OpFunction %void None %void_fn + %entry = OpLabel + %unused = OpBitReverse %u32 %u32_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRBitInstructions, RequiresExtension) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability BitInstructions + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_1 = OpConstant %u32 1 + + %main = OpFunction %void None %void_fn + %entry = OpLabel + %unused = OpBitReverse %u32 %u32_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("1st operand of Capability: operand BitInstructions(6025) " + "requires one of these extensions: SPV_KHR_bit_instructions")); +} + +TEST_F(ValidateSpvKHRBitInstructions, RequiresCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpExtension "SPV_KHR_bit_instructions" + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_1 = OpConstant %u32 1 + + %main = OpFunction %void None %void_fn + %entry = OpLabel + %unused = OpBitReverse %u32 %u32_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode BitReverse requires one of these capabilities: " + "Shader BitInstructions")); +} + +} // namespace +} // namespace val +} // namespace spvtools -- cgit v1.2.3 From a95bc460f924a9541dc3a85745dcb15140c8fcf3 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Fri, 2 Jul 2021 10:18:19 -0400 Subject: Add remove_unused_interface_variable_pass.* to BUILD.gn (#4363) --- BUILD.gn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BUILD.gn b/BUILD.gn index 19ee77ac..9587f6a3 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -676,6 +676,8 @@ static_library("spvtools_opt") { "source/opt/relax_float_ops_pass.h", "source/opt/remove_duplicates_pass.cpp", "source/opt/remove_duplicates_pass.h", + "source/opt/remove_unused_interface_variables_pass.cpp", + "source/opt/remove_unused_interface_variables_pass.h", "source/opt/replace_invalid_opc.cpp", "source/opt/replace_invalid_opc.h", "source/opt/scalar_analysis.cpp", -- cgit v1.2.3 From 7763360524ecd75703edad2c4e1fb4f6e047ecf7 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Sun, 4 Jul 2021 11:26:11 +0000 Subject: spirv-fuzz: Added tests for signedness analysis (#4361) Adds skeleton tests to support #4345. --- ...transformation_replace_id_with_synonym_test.cpp | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 66c7ed09..c0739c3b 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -2031,6 +2031,65 @@ TEST(TransformationReplaceIdWithSynonymTest, ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this +// test so that it covers more atomic operations, and enable the test once the +// issue is fixed. +TEST(TransformationReplaceIdWithSynonymTest, DISABLED_TypesAreCompatible) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeInt 32 0 + %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); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const uint32_t int_type = 6; // The id of OpTypeInt 32 1 + const uint32_t uint_type = 9; // The id of OpTypeInt 32 0 + + // OpAtomicLoad +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicLoad, 0, int_type, uint_type), + "Invalid operand index"); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicLoad, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicLoad, 2, int_type, uint_type)); + + // OpAtomicExchange +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 0, int_type, uint_type), + "Invalid operand index"); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 2, int_type, uint_type)); + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Similar for + // other atomic instructions +} + } // namespace } // namespace fuzz } // namespace spvtools -- cgit v1.2.3 From 4d2832e3c8d729bf39a5e5482077a27125735a23 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 5 Jul 2021 21:09:43 +0000 Subject: spirv-fuzz: Check updated analyses in transformation tests (#4266) Enhances the tests for TransformationSwapFunctionVariables to check that analyses are updated. --- test/fuzz/transformation_swap_two_functions_test.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/fuzz/transformation_swap_two_functions_test.cpp b/test/fuzz/transformation_swap_two_functions_test.cpp index 38d6a076..20342526 100644 --- a/test/fuzz/transformation_swap_two_functions_test.cpp +++ b/test/fuzz/transformation_swap_two_functions_test.cpp @@ -154,7 +154,20 @@ TEST(TransformationSwapTwoFunctionsTest, SimpleTest) { // Function with result_id 10 and 13 should swap successfully. auto swap_test5 = TransformationSwapTwoFunctions(10, 13); ASSERT_TRUE(swap_test5.IsApplicable(context.get(), transformation_context)); + + // Get the definitions of functions 10 and 13, as recorded by the def-use + // manager. + auto def_use_manager = context->get_def_use_mgr(); + auto function_10_inst = def_use_manager->GetDef(10); + auto function_13_inst = def_use_manager->GetDef(13); + ApplyAndCheckFreshIds(swap_test5, context.get(), &transformation_context); + + // Check that def-use information for functions 10 and 13 has been preserved + // by the transformation. + ASSERT_EQ(function_10_inst, def_use_manager->GetDef(10)); + ASSERT_EQ(function_13_inst, def_use_manager->GetDef(13)); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); -- cgit v1.2.3 From 9ce7a2fb62eb843882fd39c24c08d27d503fa378 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 6 Jul 2021 06:14:36 +0000 Subject: spirv-reduce: Eliminate skeletal structured control flow construct (#4360) This change allows spriv-reduce to get rid of a selection, switch or loop construct if none of the instructions defined in the construct are used outside the construct. --- BUILD.gn | 4 + source/reduce/CMakeLists.txt | 4 + source/reduce/reducer.cpp | 5 +- ...ed_construct_to_block_reduction_opportunity.cpp | 67 ++++++ ...ured_construct_to_block_reduction_opportunity.h | 49 +++++ ...truct_to_block_reduction_opportunity_finder.cpp | 185 ++++++++++++++++ ...nstruct_to_block_reduction_opportunity_finder.h | 57 +++++ test/reduce/CMakeLists.txt | 5 +- test/reduce/reduce_test_util.cpp | 28 ++- test/reduce/structured_construct_to_block_test.cpp | 245 +++++++++++++++++++++ 10 files changed, 644 insertions(+), 5 deletions(-) create mode 100644 source/reduce/structured_construct_to_block_reduction_opportunity.cpp create mode 100644 source/reduce/structured_construct_to_block_reduction_opportunity.h create mode 100644 source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp create mode 100644 source/reduce/structured_construct_to_block_reduction_opportunity_finder.h create mode 100644 test/reduce/structured_construct_to_block_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 9587f6a3..996905a2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -803,6 +803,10 @@ static_library("spvtools_reduce") { "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h", "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp", "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h", + "source/reduce/structured_construct_to_block_reduction_opportunity.cpp", + "source/reduce/structured_construct_to_block_reduction_opportunity.h", + "source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp", + "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h", "source/reduce/structured_loop_to_selection_reduction_opportunity.cpp", "source/reduce/structured_loop_to_selection_reduction_opportunity.h", "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp", diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt index 30432431..6fd8409f 100644 --- a/source/reduce/CMakeLists.txt +++ b/source/reduce/CMakeLists.txt @@ -38,6 +38,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_unused_struct_member_reduction_opportunity_finder.h simple_conditional_branch_to_branch_opportunity_finder.h simple_conditional_branch_to_branch_reduction_opportunity.h + structured_construct_to_block_reduction_opportunity.h + structured_construct_to_block_reduction_opportunity_finder.h structured_loop_to_selection_reduction_opportunity.h structured_loop_to_selection_reduction_opportunity_finder.h @@ -67,6 +69,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_unused_struct_member_reduction_opportunity_finder.cpp simple_conditional_branch_to_branch_opportunity_finder.cpp simple_conditional_branch_to_branch_reduction_opportunity.cpp + structured_construct_to_block_reduction_opportunity.cpp + structured_construct_to_block_reduction_opportunity_finder.cpp structured_loop_to_selection_reduction_opportunity.cpp structured_loop_to_selection_reduction_opportunity_finder.cpp ) diff --git a/source/reduce/reducer.cpp b/source/reduce/reducer.cpp index 16bb94fe..b752f415 100644 --- a/source/reduce/reducer.cpp +++ b/source/reduce/reducer.cpp @@ -28,6 +28,7 @@ #include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" #include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h" #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" +#include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h" #include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" #include "source/spirv_reducer_options.h" @@ -112,6 +113,8 @@ void Reducer::AddDefaultReductionPasses() { spvtools::MakeUnique()); AddReductionPass( spvtools::MakeUnique()); + AddReductionPass(spvtools::MakeUnique< + StructuredConstructToBlockReductionOpportunityFinder>()); AddReductionPass(spvtools::MakeUnique< StructuredLoopToSelectionReductionOpportunityFinder>()); AddReductionPass( @@ -141,7 +144,7 @@ void Reducer::AddReductionPass( std::unique_ptr finder) { passes_.push_back( spvtools::MakeUnique(target_env_, std::move(finder))); -} +} void Reducer::AddCleanupReductionPass( std::unique_ptr finder) { diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp new file mode 100644 index 00000000..ed738411 --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_construct_to_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +bool StructuredConstructToBlockReductionOpportunity::PreconditionHolds() { + return context_->get_def_use_mgr()->GetDef(construct_header_) != nullptr; +} + +void StructuredConstructToBlockReductionOpportunity::Apply() { + auto header_block = context_->cfg()->block(construct_header_); + auto merge_block = context_->cfg()->block(header_block->MergeBlockId()); + + auto* enclosing_function = header_block->GetParent(); + + // A region of blocks is defined in terms of dominators and post-dominators, + // so we compute these for the enclosing function. + auto* dominators = context_->GetDominatorAnalysis(enclosing_function); + auto* postdominators = context_->GetPostDominatorAnalysis(enclosing_function); + + // For each block in the function, determine whether it is inside the region. + // If it is, delete it. + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + if (header_block != &*block_it && merge_block != &*block_it && + dominators->Dominates(header_block, &*block_it) && + postdominators->Dominates(merge_block, &*block_it)) { + block_it = block_it.Erase(); + } else { + ++block_it; + } + } + // Having removed some blocks from the module it is necessary to invalidate + // analyses, since the remaining patch-up work depends on various analyses + // which will otherwise reference blocks that have been deleted. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // We demote the header of the region to a regular block by deleting its merge + // instruction. + context_->KillInst(header_block->GetMergeInst()); + + // The terminator for the header block is changed to be an unconditional + // branch to the merge block. + header_block->terminator()->SetOpcode(SpvOpBranch); + header_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {merge_block->id()}}}); + + // This is an intrusive change, so we invalidate all analyses. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity.h b/source/reduce/structured_construct_to_block_reduction_opportunity.h new file mode 100644 index 00000000..f461a2f0 --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity.h @@ -0,0 +1,49 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace a skeletal structured control flow construct with a +// single block. +class StructuredConstructToBlockReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity from a header block id. + StructuredConstructToBlockReductionOpportunity(opt::IRContext* context, + uint32_t construct_header) + : context_(context), construct_header_(construct_header) {} + + // Returns true if and only if |construct_header_| exists in the module - + // another opportunity may have removed it. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + uint32_t construct_header_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..dc20f689 --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h" + +#include + +#include "source/reduce/structured_construct_to_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +StructuredConstructToBlockReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every function in the module. + for (auto* function : GetTargetFunctions(context, target_function)) { + // For every header block in the function, there is potentially a region of + // blocks that could be collapsed. + std::unordered_map> + regions; + + // Regions are identified using dominators and postdominators, so we compute + // those for the function. + auto* dominators = context->GetDominatorAnalysis(function); + auto* postdominators = context->GetPostDominatorAnalysis(function); + + // Consider every block in the function. + for (auto& block : *function) { + // If a block has an unreachable predecessor then folding away a region in + // which that block is contained gets complicated, so we ignore regions + // that contain such blocks. We note whether this block suffers from this + // problem. + bool has_unreachable_predecessor = + HasUnreachablePredecessor(block, context); + + // Look through all the regions we have identified so far to see whether + // this block is part of a region, or spoils a region (by having an + // unreachable predecessor). + for (auto entry = regions.begin(); entry != regions.end();) { + // |block| is in this region if it is dominated by the header, + // post-dominated by the merge, and different from the merge. + assert(&block != entry->first && + "The block should not be the region's header because we only " + "make a region when we encounter its header."); + if (entry->first->MergeBlockId() != block.id() && + dominators->Dominates(entry->first, &block) && + postdominators->Dominates( + entry->first->GetMergeInst()->GetSingleWordInOperand(0), + block.id())) { + if (has_unreachable_predecessor) { + // The block would be in this region, but it has an unreachable + // predecessor. This spoils the region, so we remove it. + entry = regions.erase(entry); + continue; + } else { + // Add the block to the region. + entry->second.insert(&block); + } + } + ++entry; + } + if (block.MergeBlockIdIfAny() == 0) { + // The block isn't a header, so it doesn't constitute a new region. + continue; + } + if (!context->IsReachable(block)) { + // The block isn't reachable, so it doesn't constitute a new region. + continue; + } + auto* merge_block = context->cfg()->block( + block.GetMergeInst()->GetSingleWordInOperand(0)); + if (!context->IsReachable(*merge_block)) { + // The block's merge is unreachable, so it doesn't constitute a new + // region. + continue; + } + assert(dominators->Dominates(&block, merge_block) && + "The merge block is reachable, so the header must dominate it"); + if (!postdominators->Dominates(merge_block, &block)) { + // The block is not post-dominated by its merge. This happens for + // instance when there is a break from a conditional, or an early exit. + // This also means that we don't add a region. + continue; + } + // We have a reachable header block with a rechable merge that + // postdominates the header: this means we have a new region. + regions.emplace(&block, std::unordered_set()); + } + + // Now that we have found all the regions and blocks within them, we check + // whether any region defines an id that is used outside the region. If this + // is *not* the case, then we have an opportunity to collapse the region + // down to its header block and merge block. + for (auto& entry : regions) { + if (DefinitionsRestrictedToRegion(*entry.first, entry.second, context)) { + result.emplace_back( + MakeUnique( + context, entry.first->id())); + } + } + } + return result; +} + +bool StructuredConstructToBlockReductionOpportunityFinder:: + DefinitionsRestrictedToRegion( + const opt::BasicBlock& header, + const std::unordered_set& region, + opt::IRContext* context) { + // Consider every block in the region. + for (auto& block : region) { + // Consider every instruction in the block - this includes the label + // instruction + if (!block->WhileEachInst( + [context, &header, ®ion](opt::Instruction* inst) -> bool { + if (inst->result_id() == 0) { + // The instruction does not genreate a result id, thus it cannot + // be referred to outside the region - this is fine. + return true; + } + // Consider every use of the instruction's result id. + if (!context->get_def_use_mgr()->WhileEachUse( + inst->result_id(), + [context, &header, ®ion](opt::Instruction* user, + uint32_t) -> bool { + auto user_block = context->get_instr_block(user); + if (user == header.GetMergeInst() || + user == header.terminator()) { + // We are going to delete the header's merge + // instruction and rewrite its terminator, so it does + // not matter if the user is one of these + // instructions. + return true; + } + if (user_block == nullptr || + region.count(user_block) == 0) { + // The user is either a global instruction, or an + // instruction in a block outside the region. Removing + // the region would invalidate this user. + return false; + } + return true; + })) { + return false; + } + return true; + })) { + return false; + } + } + return true; +} + +bool StructuredConstructToBlockReductionOpportunityFinder:: + HasUnreachablePredecessor(const opt::BasicBlock& block, + opt::IRContext* context) { + for (auto pred : context->cfg()->preds(block.id())) { + if (!context->IsReachable(*context->cfg()->block(pred))) { + return true; + } + } + return false; +} + +std::string StructuredConstructToBlockReductionOpportunityFinder::GetName() + const { + return "StructuredConstructToBlockReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h new file mode 100644 index 00000000..28bbc17c --- /dev/null +++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H +#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to replace a skeletal structured control flow +// construct - that is, a construct that does not define anything that's used +// outside the construct - into its header block. +class StructuredConstructToBlockReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + StructuredConstructToBlockReductionOpportunityFinder() = default; + + ~StructuredConstructToBlockReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // Returns true if and only if all instructions defined in |region| are used + // only inside |region|, with the exception that they may be used by the merge + // or terminator instruction of |header|, which must be the header block for + // the region. + static bool DefinitionsRestrictedToRegion( + const opt::BasicBlock& header, + const std::unordered_set& region, + opt::IRContext* context); + + // Returns true if and only if |block| has at least one predecessor that is + // unreachable in the control flow graph of its function. + static bool HasUnreachablePredecessor(const opt::BasicBlock& block, + opt::IRContext* context); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt index 652f0ab5..121cd4f0 100644 --- a/test/reduce/CMakeLists.txt +++ b/test/reduce/CMakeLists.txt @@ -14,6 +14,7 @@ add_spvtools_unittest(TARGET reduce SRCS + conditional_branch_to_simple_conditional_branch_test.cpp merge_blocks_test.cpp operand_to_constant_test.cpp operand_to_undef_test.cpp @@ -26,10 +27,10 @@ add_spvtools_unittest(TARGET reduce remove_selection_test.cpp remove_unused_instruction_test.cpp remove_unused_struct_member_test.cpp + simple_conditional_branch_to_branch_test.cpp + structured_construct_to_block_test.cpp structured_loop_to_selection_test.cpp validation_during_reduction_test.cpp - conditional_branch_to_simple_conditional_branch_test.cpp - simple_conditional_branch_to_branch_test.cpp LIBS SPIRV-Tools-reduce ) diff --git a/test/reduce/reduce_test_util.cpp b/test/reduce/reduce_test_util.cpp index 0c234111..42716600 100644 --- a/test/reduce/reduce_test_util.cpp +++ b/test/reduce/reduce_test_util.cpp @@ -21,6 +21,29 @@ namespace spvtools { namespace reduce { +const spvtools::MessageConsumer kConsoleMessageConsumer = + [](spv_message_level_t level, const char*, const spv_position_t& position, + const char* message) -> void { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +}; + void CheckEqual(const spv_target_env env, const std::vector& expected_binary, const std::vector& actual_binary) { @@ -55,8 +78,9 @@ void CheckEqual(const spv_target_env env, const std::string& expected_text, void CheckValid(spv_target_env env, const opt::IRContext* ir) { std::vector binary; ir->module()->ToBinary(&binary, false); - SpirvTools t(env); - ASSERT_TRUE(t.Validate(binary)); + SpirvTools tools(env); + tools.SetMessageConsumer(kConsoleMessageConsumer); + ASSERT_TRUE(tools.Validate(binary)); } std::string ToString(spv_target_env env, const opt::IRContext* ir) { diff --git a/test/reduce/structured_construct_to_block_test.cpp b/test/reduce/structured_construct_to_block_test.cpp new file mode 100644 index 00000000..95009660 --- /dev/null +++ b/test/reduce/structured_construct_to_block_test.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(StructuredConstructToBlockReductionPassTest, SimpleTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %19 = OpConstant %6 3 + %29 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %15 + %15 = OpLabel + %18 = OpLoad %6 %8 + %20 = OpSGreaterThan %10 %18 %19 + OpSelectionMerge %22 None + OpBranchConditional %20 %21 %22 + %21 = OpLabel + OpBranch %16 + %22 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %14 + %16 = OpLabel + %24 = OpLoad %6 %8 + OpSelectionMerge %28 None + OpSwitch %24 %27 1 %25 2 %26 + %27 = OpLabel + OpStore %8 %19 + OpBranch %28 + %25 = OpLabel + OpStore %8 %29 + OpBranch %28 + %26 = OpLabel + OpStore %8 %31 + OpBranch %28 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredConstructToBlockReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(3, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + CheckValid(env, context.get()); + + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + CheckValid(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 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %19 = OpConstant %6 3 + %29 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %16 + %16 = OpLabel + %24 = OpLoad %6 %8 + OpBranch %28 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredConstructToBlockReductionPassTest, CannotBeRemovedDueToUses) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %100 "temp" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %19 = OpConstant %6 3 + %29 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + %100 = OpCopyObject %10 %11 + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %15 + %15 = OpLabel + %18 = OpLoad %6 %8 + %20 = OpSGreaterThan %10 %18 %19 + OpSelectionMerge %22 None + OpBranchConditional %20 %21 %22 + %21 = OpLabel + OpBranch %16 + %22 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %14 + %16 = OpLabel + %101 = OpCopyObject %6 %18 + %24 = OpLoad %6 %8 + OpSelectionMerge %28 None + OpSwitch %24 %27 1 %25 2 %26 + %27 = OpLabel + OpStore %8 %19 + %102 = OpCopyObject %10 %11 + OpBranch %28 + %25 = OpLabel + OpStore %8 %29 + OpBranch %28 + %26 = OpLabel + OpStore %8 %31 + OpBranch %28 + %28 = OpLabel + %103 = OpPhi %10 %102 %27 %11 %25 %11 %26 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredConstructToBlockReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_TRUE(ops.empty()); +} + +TEST(StructuredConstructToBlockReductionPassTest, + CannotBeRemovedDueToOpPhiAtMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + %101 = OpPhi %10 %11 %5 %11 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredConstructToBlockReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_TRUE(ops.empty()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools -- cgit v1.2.3 From c26baf4c93085248ae6619b254d0d4baf9d1f4a5 Mon Sep 17 00:00:00 2001 From: y-novikov Date: Wed, 7 Jul 2021 08:20:52 -0400 Subject: Update SPIRV-Headers deps (#4369) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 5f64ecaa..718927d0 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382', 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683', - 'spirv_headers_revision': 'f95c3b3761ee1b1903f54ae69b526ed6f0edc3b9', + 'spirv_headers_revision': 'ddf3230c14c71e81fc0eae9b781cc4bcc2d1f0f5', } deps = { -- cgit v1.2.3 From 3b6abf41cc726513bd0401ebe35fb8e7a3819ce7 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Mon, 12 Jul 2021 03:51:08 -0600 Subject: Add non-semantic vulkan extended instruction set (#4362) This is based on a legacy commit which installs a local grammar. A followup commit will change to the grammar in SPIRV-Headers. Co-authored-by: baldurk --- Android.mk | 16 + BUILD.bazel | 8 + BUILD.gn | 12 + build_defs.bzl | 23 + include/spirv-tools/libspirv.h | 1 + source/CMakeLists.txt | 12 +- source/ext_inst.cpp | 8 + ...t.nonsemantic.vulkan.debuginfo.100.grammar.json | 637 +++++++++++++++++++++ 8 files changed, 715 insertions(+), 2 deletions(-) create mode 100644 source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json diff --git a/Android.mk b/Android.mk index 1bc844d4..bdcdda9a 100644 --- a/Android.mk +++ b/Android.mk @@ -181,6 +181,7 @@ SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.glsl.st SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.std.100.grammar.json SPV_DEBUGINFO_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.debuginfo.grammar.json SPV_CLDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json +SPV_VKDEBUGINFO100_GRAMMAR=$(LOCAL_PATH)/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json define gen_spvtools_grammar_tables $(call generate-file-dir,$(1)/core.insts-unified1.inc) @@ -212,6 +213,7 @@ $(LOCAL_PATH)/source/ext_inst.cpp: \ $(1)/opencl.std.insts.inc \ $(1)/debuginfo.insts.inc \ $(1)/opencl.debuginfo.100.insts.inc \ + $(1)/nonsemantic.vulkan.debuginfo.100.insts.inc \ $(1)/spv-amd-gcn-shader.insts.inc \ $(1)/spv-amd-shader-ballot.insts.inc \ $(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \ @@ -241,6 +243,7 @@ endef # We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100 $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR))) $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR))) +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticVulkanDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR))) define gen_spvtools_vendor_tables @@ -255,9 +258,22 @@ $(1)/$(2).insts.inc : \ @echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar" $(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc endef +define gen_spvtools_vendor_tables_local +$(call generate-file-dir,$(1)/$(2).insts.inc) +$(1)/$(2).insts.inc : \ + $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + $(LOCAL_PATH)/source/extinst.$(2).grammar.json + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + --extinst-vendor-grammar=$(LOCAL_PATH)/source/extinst.$(2).grammar.json \ + --vendor-insts-output=$(1)/$(2).insts.inc \ + --vendor-operand-kind-prefix=$(3) + @echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar" +$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc +endef # Vendor and debug extended instruction sets, with grammars from SPIRV-Tools source tree. $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),debuginfo,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),opencl.debuginfo.100,"CLDEBUG100_")) +$(eval $(call gen_spvtools_vendor_tables_local,$(SPVTOOLS_OUT_PATH),nonsemantic.vulkan.debuginfo.100,"VKDEBUG100_")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-gcn-shader,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-ballot,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,"")) diff --git a/BUILD.bazel b/BUILD.bazel index 52290cfb..145ef744 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -3,6 +3,7 @@ load( "COMMON_COPTS", "DEBUGINFO_GRAMMAR_JSON_FILE", "CLDEBUGINFO100_GRAMMAR_JSON_FILE", + "VKDEBUGINFO100_GRAMMAR_JSON_FILE", "TEST_COPTS", "base_test", "generate_core_tables", @@ -11,6 +12,7 @@ load( "generate_glsl_tables", "generate_opencl_tables", "generate_vendor_tables", + "generate_vendor_tables_local", "link_test", "opt_test", "reduce_test", @@ -59,12 +61,16 @@ generate_vendor_tables("debuginfo") generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_") +generate_vendor_tables_local("nonsemantic.vulkan.debuginfo.100", "VKDEBUG100_") + generate_vendor_tables("nonsemantic.clspvreflection") generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE) generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE) +generate_extinst_lang_headers("VulkanDebugInfo100", VKDEBUGINFO100_GRAMMAR_JSON_FILE) + py_binary( name = "generate_registry_tables", srcs = ["utils/generate_registry_tables.py"], @@ -101,12 +107,14 @@ cc_library( ":gen_enum_string_mapping", ":gen_extinst_lang_headers_DebugInfo", ":gen_extinst_lang_headers_OpenCLDebugInfo100", + ":gen_extinst_lang_headers_VulkanDebugInfo100", ":gen_glsl_tables_unified1", ":gen_opencl_tables_unified1", ":gen_registry_tables", ":gen_vendor_tables_debuginfo", ":gen_vendor_tables_nonsemantic_clspvreflection", ":gen_vendor_tables_opencl_debuginfo_100", + ":gen_vendor_tables_nonsemantic_vulkan_debuginfo_100", ":gen_vendor_tables_spv_amd_gcn_shader", ":gen_vendor_tables_spv_amd_shader_ballot", ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter", diff --git a/BUILD.gn b/BUILD.gn index 996905a2..f3c0d5e5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -275,6 +275,10 @@ spvtools_language_header("cldebuginfo100") { name = "OpenCLDebugInfo100" grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" } +spvtools_language_header("vkdebuginfo100") { + name = "NonSemanticVulkanDebugInfo100" + grammar_file = "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json" +} spvtools_vendor_tables = [ [ @@ -301,6 +305,10 @@ spvtools_vendor_tables = [ "opencl.debuginfo.100", "CLDEBUG100_", ], + [ + "nonsemantic.vulkan.debuginfo.100", + "VKDEBUG100_", + ], [ "nonsemantic.clspvreflection", "...nil...", @@ -357,6 +365,7 @@ static_library("spvtools") { ":spvtools_generators_inc", ":spvtools_glsl_tables_glsl1-0", ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", ":spvtools_opencl_tables_opencl1-0", ] @@ -498,6 +507,7 @@ static_library("spvtools_val") { deps = [ ":spvtools", ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", ] public_deps = [ ":spvtools_headers" ] @@ -722,6 +732,7 @@ static_library("spvtools_opt") { deps = [ ":spvtools", ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", ":spvtools_vendor_tables_spv-amd-shader-ballot", ] @@ -904,6 +915,7 @@ if (build_with_chromium) { deps = [ ":spvtools", ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", ":spvtools_val", "//testing/gmock", diff --git a/build_defs.bzl b/build_defs.bzl index 30af3bd6..638b16dd 100644 --- a/build_defs.bzl +++ b/build_defs.bzl @@ -41,6 +41,7 @@ TEST_COPTS = COMMON_COPTS + select({ DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1" CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1" +VKDEBUGINFO100_GRAMMAR_JSON_FILE = "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json" def generate_core_tables(version = None): if not version: @@ -164,6 +165,28 @@ def generate_vendor_tables(extension, operand_kind_prefix = ""): visibility = ["//visibility:private"], ) +def generate_vendor_tables_local(extension, operand_kind_prefix = ""): + if not extension: + fail("Must specify extension", "extension") + extension_rule = extension.replace("-", "_").replace(".", "_") + grammars = ["source/extinst.{}.grammar.json".format(extension)] + outs = ["{}.insts.inc".format(extension)] + prefices = [operand_kind_prefix] + fmtargs = grammars + outs + prefices + native.genrule( + name = "gen_vendor_tables_" + extension_rule, + srcs = grammars, + outs = outs, + cmd = ( + "$(location :generate_grammar_tables) " + + "--extinst-vendor-grammar=$(location {0}) " + + "--vendor-insts-output=$(location {1}) " + + "--vendor-operand-kind-prefix={2}" + ).format(*fmtargs), + tools = [":generate_grammar_tables"], + visibility = ["//visibility:private"], + ) + def generate_extinst_lang_headers(name, grammar = None): if not grammar: fail("Must specify grammar", "grammar") diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 039dab18..999589e2 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -309,6 +309,7 @@ typedef enum spv_ext_inst_type_t { SPV_EXT_INST_TYPE_DEBUGINFO, SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100, // Multiple distinct extended instruction set types could return this // value, if they are prefixed with NonSemantic. and are otherwise diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 6633bc91..163505fe 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -17,11 +17,14 @@ set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_sy set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py") set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py") -# For now, assume the DebugInfo grammar file is in the current directory. -# It might migrate to SPIRV-Headers. +# Pull in grammar files that have migrated to SPIRV-Headers set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json") set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json") +# For now, assume the NonSemantic.Vulkan.DebugInfo grammar file is in the current directory. +# It will later migrate to SPIRV-Headers. +set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json") + # macro() definitions are used in the following because we need to append .inc # file paths into some global lists (*_CPP_DEPENDS). And those global lists are # later used by set_source_files_properties() calls. @@ -113,6 +116,9 @@ endmacro(spvtools_opencl_tables) macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json") + if(NOT EXISTS ${GRAMMAR_FILE}) + set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") + endif() add_custom_command(OUTPUT ${INSTS_FILE} COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} --extinst-vendor-grammar=${GRAMMAR_FILE} @@ -148,9 +154,11 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") spvtools_vendor_tables("debuginfo" "debuginfo" "") spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") +spvtools_vendor_tables("nonsemantic.vulkan.debuginfo.100" "vkdi100" "VKDEBUG100_") spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("NonSemanticVulkanDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) spvtools_vimsyntax("unified1" "1.0") add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 795cb0f3..f5f43316 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -29,6 +29,7 @@ #include "debuginfo.insts.inc" #include "glsl.std.450.insts.inc" #include "nonsemantic.clspvreflection.insts.inc" +#include "nonsemantic.vulkan.debuginfo.100.insts.inc" #include "opencl.debuginfo.100.insts.inc" #include "opencl.std.insts.inc" @@ -55,6 +56,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = { debuginfo_entries}, {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100, + ARRAY_SIZE(nonsemantic_vulkan_debuginfo_100_entries), + nonsemantic_vulkan_debuginfo_100_entries}, {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, ARRAY_SIZE(nonsemantic_clspvreflection_entries), nonsemantic_clspvreflection_entries}, @@ -126,6 +130,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { if (!strcmp("OpenCL.DebugInfo.100", name)) { return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; } + if (!strcmp("NonSemantic.Vulkan.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100; + } if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; } @@ -139,6 +146,7 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { return true; } diff --git a/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json b/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json new file mode 100644 index 00000000..371aa423 --- /dev/null +++ b/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json @@ -0,0 +1,637 @@ +{ + "copyright" : [ + "Copyright (c) 2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'Version'" }, + { "kind" : "IdRef", "name" : "'DWARF Version'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Language'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Encoding'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Storage Class'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Type Qualifier'" } + ] + }, + { + "opname" : "DebugTypeArray", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeVector", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeEnum", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Underlying Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeMember", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePtrToMember", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'Member Type'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeTemplate", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeTemplateParameter", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Actual Type'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugGlobalVariable", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDeclaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Discriminator'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugScope", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugNoScope", + "opcode" : 24 + }, + { + "opname" : "DebugInlinedAt", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLocalVariable", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Arg Number'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugInlinedVariable", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Inlined'" } + ] + }, + { + "opname" : "DebugDeclare", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Expression'" } + ] + }, + { + "opname" : "DebugValue", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugOperation", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'OpCode'" }, + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + }, + { + "opname" : "DebugImportedEntity", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Entity'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugSource", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDefinition", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Definition'" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "FlagIsProtected", + "value" : "0x01" + }, + { + "enumerant" : "FlagIsPrivate", + "value" : "0x02" + }, + { + "enumerant" : "FlagIsPublic", + "value" : "0x03" + }, + { + "enumerant" : "FlagIsLocal", + "value" : "0x04" + }, + { + "enumerant" : "FlagIsDefinition", + "value" : "0x08" + }, + { + "enumerant" : "FlagFwdDecl", + "value" : "0x10" + }, + { + "enumerant" : "FlagArtificial", + "value" : "0x20" + }, + { + "enumerant" : "FlagExplicit", + "value" : "0x40" + }, + { + "enumerant" : "FlagPrototyped", + "value" : "0x80" + }, + { + "enumerant" : "FlagObjectPointer", + "value" : "0x100" + }, + { + "enumerant" : "FlagStaticMember", + "value" : "0x200" + }, + { + "enumerant" : "FlagIndirectVariable", + "value" : "0x400" + }, + { + "enumerant" : "FlagLValueReference", + "value" : "0x800" + }, + { + "enumerant" : "FlagRValueReference", + "value" : "0x1000" + }, + { + "enumerant" : "FlagIsOptimized", + "value" : "0x2000" + }, + { + "enumerant" : "FlagIsEnumClass", + "value" : "0x4000" + }, + { + "enumerant" : "FlagTypePassByValue", + "value" : "0x8000" + }, + { + "enumerant" : "FlagTypePassByReference", + "value" : "0x10000" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugBaseTypeAttributeEncoding", + "enumerants" : [ + { + "enumerant" : "Unspecified", + "value" : "0" + }, + { + "enumerant" : "Address", + "value" : "1" + }, + { + "enumerant" : "Boolean", + "value" : "2" + }, + { + "enumerant" : "Float", + "value" : "3" + }, + { + "enumerant" : "Signed", + "value" : "4" + }, + { + "enumerant" : "SignedChar", + "value" : "5" + }, + { + "enumerant" : "Unsigned", + "value" : "6" + }, + { + "enumerant" : "UnsignedChar", + "value" : "7" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugCompositeType", + "enumerants" : [ + { + "enumerant" : "Class", + "value" : "0" + }, + { + "enumerant" : "Structure", + "value" : "1" + }, + { + "enumerant" : "Union", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugTypeQualifier", + "enumerants" : [ + { + "enumerant" : "ConstType", + "value" : "0" + }, + { + "enumerant" : "VolatileType", + "value" : "1" + }, + { + "enumerant" : "RestrictType", + "value" : "2" + }, + { + "enumerant" : "AtomicType", + "value" : "3" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugOperation", + "enumerants" : [ + { + "enumerant" : "Deref", + "value" : "0" + }, + { + "enumerant" : "Plus", + "value" : "1" + }, + { + "enumerant" : "Minus", + "value" : "2" + }, + { + "enumerant" : "PlusUconst", + "value" : "3", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Fragment", + "value" : "9", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugImportedEntity", + "enumerants" : [ + { + "enumerant" : "ImportedModule", + "value" : "0" + }, + { + "enumerant" : "ImportedDeclaration", + "value" : "1" + } + ] + } + ] +} -- cgit v1.2.3 From d432bebb113d3b804cbee769817906175e0b7544 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Tue, 13 Jul 2021 00:20:32 -0600 Subject: Fix vendor table build in BUILD.gn for nonsemantic.vulkan.debuginfo.100 (#4375) Fixes #4374 --- BUILD.gn | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index f3c0d5e5..987a33bb 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -224,6 +224,30 @@ template("spvtools_vendor_table") { } } +template("spvtools_vendor_table_local") { + assert(defined(invoker.name), "Need name in $target_name generation.") + + action("spvtools_vendor_tables_" + target_name) { + script = "utils/generate_grammar_tables.py" + + name = invoker.name + extinst_vendor_grammar = + "source/extinst.${name}.grammar.json" + extinst_file = "${target_gen_dir}/${name}.insts.inc" + + args = [ + "--extinst-vendor-grammar", + rebase_path(extinst_vendor_grammar, root_build_dir), + "--vendor-insts-output", + rebase_path(extinst_file, root_build_dir), + "--vendor-operand-kind-prefix", + invoker.operand_kind_prefix, + ] + inputs = [ extinst_vendor_grammar ] + outputs = [ extinst_file ] + } +} + action("spvtools_generators_inc") { script = "utils/generate_registry_tables.py" @@ -305,16 +329,19 @@ spvtools_vendor_tables = [ "opencl.debuginfo.100", "CLDEBUG100_", ], - [ - "nonsemantic.vulkan.debuginfo.100", - "VKDEBUG100_", - ], [ "nonsemantic.clspvreflection", "...nil...", ], ] +spvtools_vendor_tables_local = [ + [ + "nonsemantic.vulkan.debuginfo.100", + "VKDEBUG100_", + ], +] + foreach(table_def, spvtools_vendor_tables) { spvtools_vendor_table(table_def[0]) { name = table_def[0] @@ -322,6 +349,13 @@ foreach(table_def, spvtools_vendor_tables) { } } +foreach(table_def, spvtools_vendor_tables_local) { + spvtools_vendor_table_local(table_def[0]) { + name = table_def[0] + operand_kind_prefix = table_def[1] + } +} + config("spvtools_public_config") { include_dirs = [ "include" ] } @@ -373,6 +407,10 @@ static_library("spvtools") { target_name = table_def[0] deps += [ ":spvtools_vendor_tables_$target_name" ] } + foreach(table_def, spvtools_vendor_tables_local) { + target_name = table_def[0] + deps += [ ":spvtools_vendor_tables_$target_name" ] + } sources = [ "source/assembly_grammar.cpp", -- cgit v1.2.3 From 2299b710de6cac0e883b5e4d5ef231ba8c01a9d9 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 13 Jul 2021 19:45:01 +0100 Subject: spirv-fuzz: support building using gn (#4365) Adds support for building spirv-fuzz using gn. Updates the protobuf dependency to the version used by Chromium. Fixes #4372. --- BUILD.gn | 443 ++++++++++++++++++++++++++++++++++- README.md | 2 +- kokoro/scripts/linux/build-docker.sh | 2 +- kokoro/scripts/macos/build.sh | 2 +- kokoro/scripts/windows/build.bat | 4 +- tools/fuzz/fuzz.cpp | 22 +- 6 files changed, 461 insertions(+), 14 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 987a33bb..4c58233c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -14,6 +14,7 @@ import("//build_overrides/build.gni") import("//build_overrides/spirv_tools.gni") +import("//third_party/protobuf/proto_library.gni") if (build_with_chromium) { import("//testing/test.gni") } @@ -449,8 +450,12 @@ static_library("spvtools") { "source/spirv_definition.h", "source/spirv_endian.cpp", "source/spirv_endian.h", + "source/spirv_fuzzer_options.cpp", + "source/spirv_fuzzer_options.h", "source/spirv_optimizer_options.cpp", "source/spirv_optimizer_options.h", + "source/spirv_reducer_options.cpp", + "source/spirv_reducer_options.h", "source/spirv_target_env.cpp", "source/spirv_target_env.h", "source/spirv_validator_options.cpp", @@ -860,8 +865,6 @@ static_library("spvtools_reduce") { "source/reduce/structured_loop_to_selection_reduction_opportunity.h", "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp", "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h", - "source/spirv_reducer_options.cpp", - "source/spirv_reducer_options.h", ] deps = [ ":spvtools", @@ -875,6 +878,417 @@ static_library("spvtools_reduce") { configs += [ ":spvtools_internal_config" ] } +proto_library("spvtools_fuzz_proto") { + sources = [ + "source/fuzz/protobufs/spvtoolsfuzz.proto", + ] + generate_python = false + use_protobuf_full = true +} + +static_library("spvtools_fuzz") { + sources = [ + "source/fuzz/added_function_reducer.cpp", + "source/fuzz/added_function_reducer.h", + "source/fuzz/available_instructions.cpp", + "source/fuzz/available_instructions.h", + "source/fuzz/call_graph.cpp", + "source/fuzz/call_graph.h", + "source/fuzz/comparator_deep_blocks_first.h", + "source/fuzz/counter_overflow_id_source.cpp", + "source/fuzz/counter_overflow_id_source.h", + "source/fuzz/data_descriptor.cpp", + "source/fuzz/data_descriptor.h", + "source/fuzz/equivalence_relation.h", + "source/fuzz/fact_manager/constant_uniform_facts.cpp", + "source/fuzz/fact_manager/constant_uniform_facts.h", + "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp", + "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h", + "source/fuzz/fact_manager/dead_block_facts.cpp", + "source/fuzz/fact_manager/dead_block_facts.h", + "source/fuzz/fact_manager/fact_manager.cpp", + "source/fuzz/fact_manager/fact_manager.h", + "source/fuzz/fact_manager/irrelevant_value_facts.cpp", + "source/fuzz/fact_manager/irrelevant_value_facts.h", + "source/fuzz/fact_manager/livesafe_function_facts.cpp", + "source/fuzz/fact_manager/livesafe_function_facts.h", + "source/fuzz/force_render_red.cpp", + "source/fuzz/force_render_red.h", + "source/fuzz/fuzzer.cpp", + "source/fuzz/fuzzer.h", + "source/fuzz/fuzzer_context.cpp", + "source/fuzz/fuzzer_context.h", + "source/fuzz/fuzzer_pass.cpp", + "source/fuzz/fuzzer_pass.h", + "source/fuzz/fuzzer_pass_add_access_chains.cpp", + "source/fuzz/fuzzer_pass_add_access_chains.h", + "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h", + "source/fuzz/fuzzer_pass_add_composite_extract.cpp", + "source/fuzz/fuzzer_pass_add_composite_extract.h", + "source/fuzz/fuzzer_pass_add_composite_inserts.cpp", + "source/fuzz/fuzzer_pass_add_composite_inserts.h", + "source/fuzz/fuzzer_pass_add_composite_types.cpp", + "source/fuzz/fuzzer_pass_add_composite_types.h", + "source/fuzz/fuzzer_pass_add_copy_memory.cpp", + "source/fuzz/fuzzer_pass_add_copy_memory.h", + "source/fuzz/fuzzer_pass_add_dead_blocks.cpp", + "source/fuzz/fuzzer_pass_add_dead_blocks.h", + "source/fuzz/fuzzer_pass_add_dead_breaks.cpp", + "source/fuzz/fuzzer_pass_add_dead_breaks.h", + "source/fuzz/fuzzer_pass_add_dead_continues.cpp", + "source/fuzz/fuzzer_pass_add_dead_continues.h", + "source/fuzz/fuzzer_pass_add_equation_instructions.cpp", + "source/fuzz/fuzzer_pass_add_equation_instructions.h", + "source/fuzz/fuzzer_pass_add_function_calls.cpp", + "source/fuzz/fuzzer_pass_add_function_calls.h", + "source/fuzz/fuzzer_pass_add_global_variables.cpp", + "source/fuzz/fuzzer_pass_add_global_variables.h", + "source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp", + "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h", + "source/fuzz/fuzzer_pass_add_loads.cpp", + "source/fuzz/fuzzer_pass_add_loads.h", + "source/fuzz/fuzzer_pass_add_local_variables.cpp", + "source/fuzz/fuzzer_pass_add_local_variables.h", + "source/fuzz/fuzzer_pass_add_loop_preheaders.cpp", + "source/fuzz/fuzzer_pass_add_loop_preheaders.h", + "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h", + "source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp", + "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h", + "source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_opphi_synonyms.h", + "source/fuzz/fuzzer_pass_add_parameters.cpp", + "source/fuzz/fuzzer_pass_add_parameters.h", + "source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp", + "source/fuzz/fuzzer_pass_add_relaxed_decorations.h", + "source/fuzz/fuzzer_pass_add_stores.cpp", + "source/fuzz/fuzzer_pass_add_stores.h", + "source/fuzz/fuzzer_pass_add_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_synonyms.h", + "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp", + "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h", + "source/fuzz/fuzzer_pass_adjust_branch_weights.cpp", + "source/fuzz/fuzzer_pass_adjust_branch_weights.h", + "source/fuzz/fuzzer_pass_adjust_function_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_function_controls.h", + "source/fuzz/fuzzer_pass_adjust_loop_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_loop_controls.h", + "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp", + "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h", + "source/fuzz/fuzzer_pass_adjust_selection_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_selection_controls.h", + "source/fuzz/fuzzer_pass_apply_id_synonyms.cpp", + "source/fuzz/fuzzer_pass_apply_id_synonyms.h", + "source/fuzz/fuzzer_pass_construct_composites.cpp", + "source/fuzz/fuzzer_pass_construct_composites.h", + "source/fuzz/fuzzer_pass_copy_objects.cpp", + "source/fuzz/fuzzer_pass_copy_objects.h", + "source/fuzz/fuzzer_pass_donate_modules.cpp", + "source/fuzz/fuzzer_pass_donate_modules.h", + "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp", + "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h", + "source/fuzz/fuzzer_pass_expand_vector_reductions.cpp", + "source/fuzz/fuzzer_pass_expand_vector_reductions.h", + "source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp", + "source/fuzz/fuzzer_pass_flatten_conditional_branches.h", + "source/fuzz/fuzzer_pass_inline_functions.cpp", + "source/fuzz/fuzzer_pass_inline_functions.h", + "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp", + "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h", + "source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp", + "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h", + "source/fuzz/fuzzer_pass_invert_comparison_operators.cpp", + "source/fuzz/fuzzer_pass_invert_comparison_operators.h", + "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp", + "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h", + "source/fuzz/fuzzer_pass_merge_blocks.cpp", + "source/fuzz/fuzzer_pass_merge_blocks.h", + "source/fuzz/fuzzer_pass_merge_function_returns.cpp", + "source/fuzz/fuzzer_pass_merge_function_returns.h", + "source/fuzz/fuzzer_pass_mutate_pointers.cpp", + "source/fuzz/fuzzer_pass_mutate_pointers.h", + "source/fuzz/fuzzer_pass_obfuscate_constants.cpp", + "source/fuzz/fuzzer_pass_obfuscate_constants.h", + "source/fuzz/fuzzer_pass_outline_functions.cpp", + "source/fuzz/fuzzer_pass_outline_functions.h", + "source/fuzz/fuzzer_pass_permute_blocks.cpp", + "source/fuzz/fuzzer_pass_permute_blocks.h", + "source/fuzz/fuzzer_pass_permute_function_parameters.cpp", + "source/fuzz/fuzzer_pass_permute_function_parameters.h", + "source/fuzz/fuzzer_pass_permute_function_variables.cpp", + "source/fuzz/fuzzer_pass_permute_function_variables.h", + "source/fuzz/fuzzer_pass_permute_instructions.cpp", + "source/fuzz/fuzzer_pass_permute_instructions.h", + "source/fuzz/fuzzer_pass_permute_phi_operands.cpp", + "source/fuzz/fuzzer_pass_permute_phi_operands.h", + "source/fuzz/fuzzer_pass_propagate_instructions_down.cpp", + "source/fuzz/fuzzer_pass_propagate_instructions_down.h", + "source/fuzz/fuzzer_pass_propagate_instructions_up.cpp", + "source/fuzz/fuzzer_pass_propagate_instructions_up.h", + "source/fuzz/fuzzer_pass_push_ids_through_variables.cpp", + "source/fuzz/fuzzer_pass_push_ids_through_variables.h", + "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp", + "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h", + "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp", + "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h", + "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp", + "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h", + "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp", + "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h", + "source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp", + "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h", + "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp", + "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h", + "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp", + "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h", + "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp", + "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h", + "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp", + "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h", + "source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp", + "source/fuzz/fuzzer_pass_replace_parameter_with_global.h", + "source/fuzz/fuzzer_pass_replace_params_with_struct.cpp", + "source/fuzz/fuzzer_pass_replace_params_with_struct.h", + "source/fuzz/fuzzer_pass_split_blocks.cpp", + "source/fuzz/fuzzer_pass_split_blocks.h", + "source/fuzz/fuzzer_pass_swap_commutable_operands.cpp", + "source/fuzz/fuzzer_pass_swap_commutable_operands.h", + "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp", + "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h", + "source/fuzz/fuzzer_pass_swap_functions.cpp", + "source/fuzz/fuzzer_pass_swap_functions.h", + "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp", + "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h", + "source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp", + "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h", + "source/fuzz/fuzzer_util.cpp", + "source/fuzz/fuzzer_util.h", + "source/fuzz/id_use_descriptor.cpp", + "source/fuzz/id_use_descriptor.h", + "source/fuzz/instruction_descriptor.cpp", + "source/fuzz/instruction_descriptor.h", + "source/fuzz/instruction_message.cpp", + "source/fuzz/instruction_message.h", + "source/fuzz/overflow_id_source.cpp", + "source/fuzz/overflow_id_source.h", + "source/fuzz/pass_management/repeated_pass_instances.h", + "source/fuzz/pass_management/repeated_pass_manager.cpp", + "source/fuzz/pass_management/repeated_pass_manager.h", + "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp", + "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h", + "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp", + "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h", + "source/fuzz/pass_management/repeated_pass_manager_simple.cpp", + "source/fuzz/pass_management/repeated_pass_manager_simple.h", + "source/fuzz/pass_management/repeated_pass_recommender.cpp", + "source/fuzz/pass_management/repeated_pass_recommender.h", + "source/fuzz/pass_management/repeated_pass_recommender_standard.cpp", + "source/fuzz/pass_management/repeated_pass_recommender_standard.h", + "source/fuzz/protobufs/spirvfuzz_protobufs.h", + "source/fuzz/pseudo_random_generator.cpp", + "source/fuzz/pseudo_random_generator.h", + "source/fuzz/random_generator.cpp", + "source/fuzz/random_generator.h", + "source/fuzz/replayer.cpp", + "source/fuzz/replayer.h", + "source/fuzz/shrinker.cpp", + "source/fuzz/shrinker.h", + "source/fuzz/transformation.cpp", + "source/fuzz/transformation.h", + "source/fuzz/transformation_access_chain.cpp", + "source/fuzz/transformation_access_chain.h", + "source/fuzz/transformation_add_bit_instruction_synonym.cpp", + "source/fuzz/transformation_add_bit_instruction_synonym.h", + "source/fuzz/transformation_add_constant_boolean.cpp", + "source/fuzz/transformation_add_constant_boolean.h", + "source/fuzz/transformation_add_constant_composite.cpp", + "source/fuzz/transformation_add_constant_composite.h", + "source/fuzz/transformation_add_constant_null.cpp", + "source/fuzz/transformation_add_constant_null.h", + "source/fuzz/transformation_add_constant_scalar.cpp", + "source/fuzz/transformation_add_constant_scalar.h", + "source/fuzz/transformation_add_copy_memory.cpp", + "source/fuzz/transformation_add_copy_memory.h", + "source/fuzz/transformation_add_dead_block.cpp", + "source/fuzz/transformation_add_dead_block.h", + "source/fuzz/transformation_add_dead_break.cpp", + "source/fuzz/transformation_add_dead_break.h", + "source/fuzz/transformation_add_dead_continue.cpp", + "source/fuzz/transformation_add_dead_continue.h", + "source/fuzz/transformation_add_early_terminator_wrapper.cpp", + "source/fuzz/transformation_add_early_terminator_wrapper.h", + "source/fuzz/transformation_add_function.cpp", + "source/fuzz/transformation_add_function.h", + "source/fuzz/transformation_add_global_undef.cpp", + "source/fuzz/transformation_add_global_undef.h", + "source/fuzz/transformation_add_global_variable.cpp", + "source/fuzz/transformation_add_global_variable.h", + "source/fuzz/transformation_add_image_sample_unused_components.cpp", + "source/fuzz/transformation_add_image_sample_unused_components.h", + "source/fuzz/transformation_add_local_variable.cpp", + "source/fuzz/transformation_add_local_variable.h", + "source/fuzz/transformation_add_loop_preheader.cpp", + "source/fuzz/transformation_add_loop_preheader.h", + "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp", + "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h", + "source/fuzz/transformation_add_no_contraction_decoration.cpp", + "source/fuzz/transformation_add_no_contraction_decoration.h", + "source/fuzz/transformation_add_opphi_synonym.cpp", + "source/fuzz/transformation_add_opphi_synonym.h", + "source/fuzz/transformation_add_parameter.cpp", + "source/fuzz/transformation_add_parameter.h", + "source/fuzz/transformation_add_relaxed_decoration.cpp", + "source/fuzz/transformation_add_relaxed_decoration.h", + "source/fuzz/transformation_add_spec_constant_op.cpp", + "source/fuzz/transformation_add_spec_constant_op.h", + "source/fuzz/transformation_add_synonym.cpp", + "source/fuzz/transformation_add_synonym.h", + "source/fuzz/transformation_add_type_array.cpp", + "source/fuzz/transformation_add_type_array.h", + "source/fuzz/transformation_add_type_boolean.cpp", + "source/fuzz/transformation_add_type_boolean.h", + "source/fuzz/transformation_add_type_float.cpp", + "source/fuzz/transformation_add_type_float.h", + "source/fuzz/transformation_add_type_function.cpp", + "source/fuzz/transformation_add_type_function.h", + "source/fuzz/transformation_add_type_int.cpp", + "source/fuzz/transformation_add_type_int.h", + "source/fuzz/transformation_add_type_matrix.cpp", + "source/fuzz/transformation_add_type_matrix.h", + "source/fuzz/transformation_add_type_pointer.cpp", + "source/fuzz/transformation_add_type_pointer.h", + "source/fuzz/transformation_add_type_struct.cpp", + "source/fuzz/transformation_add_type_struct.h", + "source/fuzz/transformation_add_type_vector.cpp", + "source/fuzz/transformation_add_type_vector.h", + "source/fuzz/transformation_adjust_branch_weights.cpp", + "source/fuzz/transformation_adjust_branch_weights.h", + "source/fuzz/transformation_composite_construct.cpp", + "source/fuzz/transformation_composite_construct.h", + "source/fuzz/transformation_composite_extract.cpp", + "source/fuzz/transformation_composite_extract.h", + "source/fuzz/transformation_composite_insert.cpp", + "source/fuzz/transformation_composite_insert.h", + "source/fuzz/transformation_compute_data_synonym_fact_closure.cpp", + "source/fuzz/transformation_compute_data_synonym_fact_closure.h", + "source/fuzz/transformation_context.cpp", + "source/fuzz/transformation_context.h", + "source/fuzz/transformation_duplicate_region_with_selection.cpp", + "source/fuzz/transformation_duplicate_region_with_selection.h", + "source/fuzz/transformation_equation_instruction.cpp", + "source/fuzz/transformation_equation_instruction.h", + "source/fuzz/transformation_expand_vector_reduction.cpp", + "source/fuzz/transformation_expand_vector_reduction.h", + "source/fuzz/transformation_flatten_conditional_branch.cpp", + "source/fuzz/transformation_flatten_conditional_branch.h", + "source/fuzz/transformation_function_call.cpp", + "source/fuzz/transformation_function_call.h", + "source/fuzz/transformation_inline_function.cpp", + "source/fuzz/transformation_inline_function.h", + "source/fuzz/transformation_invert_comparison_operator.cpp", + "source/fuzz/transformation_invert_comparison_operator.h", + "source/fuzz/transformation_load.cpp", + "source/fuzz/transformation_load.h", + "source/fuzz/transformation_make_vector_operation_dynamic.cpp", + "source/fuzz/transformation_make_vector_operation_dynamic.h", + "source/fuzz/transformation_merge_blocks.cpp", + "source/fuzz/transformation_merge_blocks.h", + "source/fuzz/transformation_merge_function_returns.cpp", + "source/fuzz/transformation_merge_function_returns.h", + "source/fuzz/transformation_move_block_down.cpp", + "source/fuzz/transformation_move_block_down.h", + "source/fuzz/transformation_move_instruction_down.cpp", + "source/fuzz/transformation_move_instruction_down.h", + "source/fuzz/transformation_mutate_pointer.cpp", + "source/fuzz/transformation_mutate_pointer.h", + "source/fuzz/transformation_outline_function.cpp", + "source/fuzz/transformation_outline_function.h", + "source/fuzz/transformation_permute_function_parameters.cpp", + "source/fuzz/transformation_permute_function_parameters.h", + "source/fuzz/transformation_permute_phi_operands.cpp", + "source/fuzz/transformation_permute_phi_operands.h", + "source/fuzz/transformation_propagate_instruction_down.cpp", + "source/fuzz/transformation_propagate_instruction_down.h", + "source/fuzz/transformation_propagate_instruction_up.cpp", + "source/fuzz/transformation_propagate_instruction_up.h", + "source/fuzz/transformation_push_id_through_variable.cpp", + "source/fuzz/transformation_push_id_through_variable.h", + "source/fuzz/transformation_record_synonymous_constants.cpp", + "source/fuzz/transformation_record_synonymous_constants.h", + "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp", + "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h", + "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp", + "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h", + "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp", + "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h", + "source/fuzz/transformation_replace_constant_with_uniform.cpp", + "source/fuzz/transformation_replace_constant_with_uniform.h", + "source/fuzz/transformation_replace_copy_memory_with_load_store.cpp", + "source/fuzz/transformation_replace_copy_memory_with_load_store.h", + "source/fuzz/transformation_replace_copy_object_with_store_load.cpp", + "source/fuzz/transformation_replace_copy_object_with_store_load.h", + "source/fuzz/transformation_replace_id_with_synonym.cpp", + "source/fuzz/transformation_replace_id_with_synonym.h", + "source/fuzz/transformation_replace_irrelevant_id.cpp", + "source/fuzz/transformation_replace_irrelevant_id.h", + "source/fuzz/transformation_replace_linear_algebra_instruction.cpp", + "source/fuzz/transformation_replace_linear_algebra_instruction.h", + "source/fuzz/transformation_replace_load_store_with_copy_memory.cpp", + "source/fuzz/transformation_replace_load_store_with_copy_memory.h", + "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp", + "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h", + "source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp", + "source/fuzz/transformation_replace_opselect_with_conditional_branch.h", + "source/fuzz/transformation_replace_parameter_with_global.cpp", + "source/fuzz/transformation_replace_parameter_with_global.h", + "source/fuzz/transformation_replace_params_with_struct.cpp", + "source/fuzz/transformation_replace_params_with_struct.h", + "source/fuzz/transformation_set_function_control.cpp", + "source/fuzz/transformation_set_function_control.h", + "source/fuzz/transformation_set_loop_control.cpp", + "source/fuzz/transformation_set_loop_control.h", + "source/fuzz/transformation_set_memory_operands_mask.cpp", + "source/fuzz/transformation_set_memory_operands_mask.h", + "source/fuzz/transformation_set_selection_control.cpp", + "source/fuzz/transformation_set_selection_control.h", + "source/fuzz/transformation_split_block.cpp", + "source/fuzz/transformation_split_block.h", + "source/fuzz/transformation_store.cpp", + "source/fuzz/transformation_store.h", + "source/fuzz/transformation_swap_commutable_operands.cpp", + "source/fuzz/transformation_swap_commutable_operands.h", + "source/fuzz/transformation_swap_conditional_branch_operands.cpp", + "source/fuzz/transformation_swap_conditional_branch_operands.h", + "source/fuzz/transformation_swap_function_variables.cpp", + "source/fuzz/transformation_swap_function_variables.h", + "source/fuzz/transformation_swap_two_functions.cpp", + "source/fuzz/transformation_swap_two_functions.h", + "source/fuzz/transformation_toggle_access_chain_instruction.cpp", + "source/fuzz/transformation_toggle_access_chain_instruction.h", + "source/fuzz/transformation_vector_shuffle.cpp", + "source/fuzz/transformation_vector_shuffle.h", + "source/fuzz/transformation_wrap_early_terminator_in_function.cpp", + "source/fuzz/transformation_wrap_early_terminator_in_function.h", + "source/fuzz/transformation_wrap_region_in_selection.cpp", + "source/fuzz/transformation_wrap_region_in_selection.h", + "source/fuzz/uniform_buffer_element_descriptor.cpp", + "source/fuzz/uniform_buffer_element_descriptor.h", + ] + deps = [ + "//third_party/protobuf:protobuf_full", + ":spvtools_fuzz_proto", + ":spvtools", + ":spvtools_opt", + ] + public_deps = [ ":spvtools_headers" ] + if (build_with_chromium) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + configs += [ ":spvtools_internal_config" ] +} + group("SPIRV-Tools") { public_deps = [ ":spvtools", @@ -1063,7 +1477,25 @@ executable("spirv-link") { } if (!is_ios && !spirv_is_winuwp) { - # iOS and UWP do not allow std::system calls which spirv-reduce requires + + # iOS and UWP do not allow std::system calls which spirv-fuzz and + # spirv-reduce require + + executable("spirv-fuzz") { + sources = [ "tools/fuzz/fuzz.cpp" ] + deps = [ + "//third_party/protobuf:protobuf_full", + ":spvtools", + ":spvtools_fuzz", + ":spvtools_reduce", + ":spvtools_opt", + ":spvtools_software_version", + ":spvtools_util_cli_consumer", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] + } + executable("spirv-reduce") { sources = [ "tools/reduce/reduce.cpp" ] deps = [ @@ -1088,6 +1520,9 @@ group("all_spirv_tools") { ":spirv-val", ] if (!is_ios && !spirv_is_winuwp) { - deps += [ ":spirv-reduce" ] + deps += [ + ":spirv-fuzz", + ":spirv-reduce", + ] } } diff --git a/README.md b/README.md index 3637305b..db52b79f 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ option, like so: ```sh # In (the SPIRV-Tools repo root): -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf # In your build directory: cmake [-G ] -DSPIRV_BUILD_FUZZER=ON diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh index ba216987..553f0759 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh @@ -51,7 +51,7 @@ clone_if_missing https://github.com/google/googletest external/googlete pushd external/googletest; git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7; popd clone_if_missing https://github.com/google/effcee external/effcee --depth=1 clone_if_missing https://github.com/google/re2 external/re2 --depth=1 -clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0 +clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0.1 if [ $TOOL = "cmake" ]; then using cmake-3.17.2 diff --git a/kokoro/scripts/macos/build.sh b/kokoro/scripts/macos/build.sh index 44c9a412..46128238 100644 --- a/kokoro/scripts/macos/build.sh +++ b/kokoro/scripts/macos/build.sh @@ -36,7 +36,7 @@ git clone https://github.com/google/googletest external/googletest cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf mkdir build && cd $SRC/build diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat index c964320d..24e29ccf 100644 --- a/kokoro/scripts/windows/build.bat +++ b/kokoro/scripts/windows/build.bat @@ -30,7 +30,7 @@ git clone https://github.com/google/googletest external/googletest cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf :: ######################################### :: set up msvc build env @@ -93,4 +93,4 @@ zip -r install.zip install rm -rf %SRC%\build rm -rf %SRC%\external -exit /b 0 \ No newline at end of file +exit /b 0 diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index 422bea53..de72b2cd 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp @@ -673,6 +673,19 @@ void DumpTransformationsBinary( transformations_file.close(); } +// The Chromium project applies the following patch to the protobuf library: +// +// source.chromium.org/chromium/chromium/src/+/main:third_party/protobuf/patches/0003-remove-static-initializers.patch +// +// This affects how Status objects must be constructed. This method provides a +// convenient way to get the OK status that works both with and without the +// patch. With the patch OK is a StatusPod, from which a Status can be +// constructed. Without the patch, OK is already a Status, and we harmlessly +// copy-construct the result from it. +google::protobuf::util::Status GetProtobufOkStatus() { + return google::protobuf::util::Status(google::protobuf::util::Status::OK); +} + // Dumps |transformations| to file |filename| in JSON format. Useful for // interactive debugging. void DumpTransformationsJson( @@ -683,7 +696,7 @@ void DumpTransformationsJson( json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations, &json_string, json_options); - if (json_generation_status == google::protobuf::util::Status::OK) { + if (json_generation_status == GetProtobufOkStatus()) { std::ofstream transformations_json_file(filename); transformations_json_file << json_string; transformations_json_file.close(); @@ -734,9 +747,8 @@ int main(int argc, const char** argv) { std::string facts_json_string((std::istreambuf_iterator(facts_input)), std::istreambuf_iterator()); facts_input.close(); - if (google::protobuf::util::Status::OK != - google::protobuf::util::JsonStringToMessage(facts_json_string, - &initial_facts)) { + if (GetProtobufOkStatus() != google::protobuf::util::JsonStringToMessage( + facts_json_string, &initial_facts)) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data"); return 1; } @@ -816,7 +828,7 @@ int main(int argc, const char** argv) { json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations_applied, &json_string, json_options); - if (json_generation_status != google::protobuf::util::Status::OK) { + if (json_generation_status != GetProtobufOkStatus()) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error writing out transformations in JSON format"); return 1; -- cgit v1.2.3 From feb05446bbc3c6d985ccc7298396a3050128ed8c Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 14 Jul 2021 10:04:50 +0100 Subject: Fix BUILD.gn (#4378) This change restricts BUILD.gn so that spirv-fuzz-related targets are only built when in a Chromium checkout. This is due to their dependence on protobuf, which is available in gn-friendly form in Chromium only. Fixes https://crbug.com/tint/987 --- BUILD.gn | 837 ++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 424 insertions(+), 413 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 4c58233c..53e2128b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -14,9 +14,9 @@ import("//build_overrides/build.gni") import("//build_overrides/spirv_tools.gni") -import("//third_party/protobuf/proto_library.gni") if (build_with_chromium) { import("//testing/test.gni") + import("//third_party/protobuf/proto_library.gni") } spirv_headers = spirv_tools_spirv_headers_dir @@ -878,415 +878,419 @@ static_library("spvtools_reduce") { configs += [ ":spvtools_internal_config" ] } -proto_library("spvtools_fuzz_proto") { - sources = [ - "source/fuzz/protobufs/spvtoolsfuzz.proto", - ] - generate_python = false - use_protobuf_full = true -} +if (build_with_chromium) { -static_library("spvtools_fuzz") { - sources = [ - "source/fuzz/added_function_reducer.cpp", - "source/fuzz/added_function_reducer.h", - "source/fuzz/available_instructions.cpp", - "source/fuzz/available_instructions.h", - "source/fuzz/call_graph.cpp", - "source/fuzz/call_graph.h", - "source/fuzz/comparator_deep_blocks_first.h", - "source/fuzz/counter_overflow_id_source.cpp", - "source/fuzz/counter_overflow_id_source.h", - "source/fuzz/data_descriptor.cpp", - "source/fuzz/data_descriptor.h", - "source/fuzz/equivalence_relation.h", - "source/fuzz/fact_manager/constant_uniform_facts.cpp", - "source/fuzz/fact_manager/constant_uniform_facts.h", - "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp", - "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h", - "source/fuzz/fact_manager/dead_block_facts.cpp", - "source/fuzz/fact_manager/dead_block_facts.h", - "source/fuzz/fact_manager/fact_manager.cpp", - "source/fuzz/fact_manager/fact_manager.h", - "source/fuzz/fact_manager/irrelevant_value_facts.cpp", - "source/fuzz/fact_manager/irrelevant_value_facts.h", - "source/fuzz/fact_manager/livesafe_function_facts.cpp", - "source/fuzz/fact_manager/livesafe_function_facts.h", - "source/fuzz/force_render_red.cpp", - "source/fuzz/force_render_red.h", - "source/fuzz/fuzzer.cpp", - "source/fuzz/fuzzer.h", - "source/fuzz/fuzzer_context.cpp", - "source/fuzz/fuzzer_context.h", - "source/fuzz/fuzzer_pass.cpp", - "source/fuzz/fuzzer_pass.h", - "source/fuzz/fuzzer_pass_add_access_chains.cpp", - "source/fuzz/fuzzer_pass_add_access_chains.h", - "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp", - "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h", - "source/fuzz/fuzzer_pass_add_composite_extract.cpp", - "source/fuzz/fuzzer_pass_add_composite_extract.h", - "source/fuzz/fuzzer_pass_add_composite_inserts.cpp", - "source/fuzz/fuzzer_pass_add_composite_inserts.h", - "source/fuzz/fuzzer_pass_add_composite_types.cpp", - "source/fuzz/fuzzer_pass_add_composite_types.h", - "source/fuzz/fuzzer_pass_add_copy_memory.cpp", - "source/fuzz/fuzzer_pass_add_copy_memory.h", - "source/fuzz/fuzzer_pass_add_dead_blocks.cpp", - "source/fuzz/fuzzer_pass_add_dead_blocks.h", - "source/fuzz/fuzzer_pass_add_dead_breaks.cpp", - "source/fuzz/fuzzer_pass_add_dead_breaks.h", - "source/fuzz/fuzzer_pass_add_dead_continues.cpp", - "source/fuzz/fuzzer_pass_add_dead_continues.h", - "source/fuzz/fuzzer_pass_add_equation_instructions.cpp", - "source/fuzz/fuzzer_pass_add_equation_instructions.h", - "source/fuzz/fuzzer_pass_add_function_calls.cpp", - "source/fuzz/fuzzer_pass_add_function_calls.h", - "source/fuzz/fuzzer_pass_add_global_variables.cpp", - "source/fuzz/fuzzer_pass_add_global_variables.h", - "source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp", - "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h", - "source/fuzz/fuzzer_pass_add_loads.cpp", - "source/fuzz/fuzzer_pass_add_loads.h", - "source/fuzz/fuzzer_pass_add_local_variables.cpp", - "source/fuzz/fuzzer_pass_add_local_variables.h", - "source/fuzz/fuzzer_pass_add_loop_preheaders.cpp", - "source/fuzz/fuzzer_pass_add_loop_preheaders.h", - "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp", - "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h", - "source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp", - "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h", - "source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp", - "source/fuzz/fuzzer_pass_add_opphi_synonyms.h", - "source/fuzz/fuzzer_pass_add_parameters.cpp", - "source/fuzz/fuzzer_pass_add_parameters.h", - "source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp", - "source/fuzz/fuzzer_pass_add_relaxed_decorations.h", - "source/fuzz/fuzzer_pass_add_stores.cpp", - "source/fuzz/fuzzer_pass_add_stores.h", - "source/fuzz/fuzzer_pass_add_synonyms.cpp", - "source/fuzz/fuzzer_pass_add_synonyms.h", - "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp", - "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h", - "source/fuzz/fuzzer_pass_adjust_branch_weights.cpp", - "source/fuzz/fuzzer_pass_adjust_branch_weights.h", - "source/fuzz/fuzzer_pass_adjust_function_controls.cpp", - "source/fuzz/fuzzer_pass_adjust_function_controls.h", - "source/fuzz/fuzzer_pass_adjust_loop_controls.cpp", - "source/fuzz/fuzzer_pass_adjust_loop_controls.h", - "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp", - "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h", - "source/fuzz/fuzzer_pass_adjust_selection_controls.cpp", - "source/fuzz/fuzzer_pass_adjust_selection_controls.h", - "source/fuzz/fuzzer_pass_apply_id_synonyms.cpp", - "source/fuzz/fuzzer_pass_apply_id_synonyms.h", - "source/fuzz/fuzzer_pass_construct_composites.cpp", - "source/fuzz/fuzzer_pass_construct_composites.h", - "source/fuzz/fuzzer_pass_copy_objects.cpp", - "source/fuzz/fuzzer_pass_copy_objects.h", - "source/fuzz/fuzzer_pass_donate_modules.cpp", - "source/fuzz/fuzzer_pass_donate_modules.h", - "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp", - "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h", - "source/fuzz/fuzzer_pass_expand_vector_reductions.cpp", - "source/fuzz/fuzzer_pass_expand_vector_reductions.h", - "source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp", - "source/fuzz/fuzzer_pass_flatten_conditional_branches.h", - "source/fuzz/fuzzer_pass_inline_functions.cpp", - "source/fuzz/fuzzer_pass_inline_functions.h", - "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp", - "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h", - "source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp", - "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h", - "source/fuzz/fuzzer_pass_invert_comparison_operators.cpp", - "source/fuzz/fuzzer_pass_invert_comparison_operators.h", - "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp", - "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h", - "source/fuzz/fuzzer_pass_merge_blocks.cpp", - "source/fuzz/fuzzer_pass_merge_blocks.h", - "source/fuzz/fuzzer_pass_merge_function_returns.cpp", - "source/fuzz/fuzzer_pass_merge_function_returns.h", - "source/fuzz/fuzzer_pass_mutate_pointers.cpp", - "source/fuzz/fuzzer_pass_mutate_pointers.h", - "source/fuzz/fuzzer_pass_obfuscate_constants.cpp", - "source/fuzz/fuzzer_pass_obfuscate_constants.h", - "source/fuzz/fuzzer_pass_outline_functions.cpp", - "source/fuzz/fuzzer_pass_outline_functions.h", - "source/fuzz/fuzzer_pass_permute_blocks.cpp", - "source/fuzz/fuzzer_pass_permute_blocks.h", - "source/fuzz/fuzzer_pass_permute_function_parameters.cpp", - "source/fuzz/fuzzer_pass_permute_function_parameters.h", - "source/fuzz/fuzzer_pass_permute_function_variables.cpp", - "source/fuzz/fuzzer_pass_permute_function_variables.h", - "source/fuzz/fuzzer_pass_permute_instructions.cpp", - "source/fuzz/fuzzer_pass_permute_instructions.h", - "source/fuzz/fuzzer_pass_permute_phi_operands.cpp", - "source/fuzz/fuzzer_pass_permute_phi_operands.h", - "source/fuzz/fuzzer_pass_propagate_instructions_down.cpp", - "source/fuzz/fuzzer_pass_propagate_instructions_down.h", - "source/fuzz/fuzzer_pass_propagate_instructions_up.cpp", - "source/fuzz/fuzzer_pass_propagate_instructions_up.h", - "source/fuzz/fuzzer_pass_push_ids_through_variables.cpp", - "source/fuzz/fuzzer_pass_push_ids_through_variables.h", - "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp", - "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h", - "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp", - "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h", - "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp", - "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h", - "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp", - "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h", - "source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp", - "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h", - "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp", - "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h", - "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp", - "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h", - "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp", - "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h", - "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp", - "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h", - "source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp", - "source/fuzz/fuzzer_pass_replace_parameter_with_global.h", - "source/fuzz/fuzzer_pass_replace_params_with_struct.cpp", - "source/fuzz/fuzzer_pass_replace_params_with_struct.h", - "source/fuzz/fuzzer_pass_split_blocks.cpp", - "source/fuzz/fuzzer_pass_split_blocks.h", - "source/fuzz/fuzzer_pass_swap_commutable_operands.cpp", - "source/fuzz/fuzzer_pass_swap_commutable_operands.h", - "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp", - "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h", - "source/fuzz/fuzzer_pass_swap_functions.cpp", - "source/fuzz/fuzzer_pass_swap_functions.h", - "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp", - "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h", - "source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp", - "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h", - "source/fuzz/fuzzer_util.cpp", - "source/fuzz/fuzzer_util.h", - "source/fuzz/id_use_descriptor.cpp", - "source/fuzz/id_use_descriptor.h", - "source/fuzz/instruction_descriptor.cpp", - "source/fuzz/instruction_descriptor.h", - "source/fuzz/instruction_message.cpp", - "source/fuzz/instruction_message.h", - "source/fuzz/overflow_id_source.cpp", - "source/fuzz/overflow_id_source.h", - "source/fuzz/pass_management/repeated_pass_instances.h", - "source/fuzz/pass_management/repeated_pass_manager.cpp", - "source/fuzz/pass_management/repeated_pass_manager.h", - "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp", - "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h", - "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp", - "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h", - "source/fuzz/pass_management/repeated_pass_manager_simple.cpp", - "source/fuzz/pass_management/repeated_pass_manager_simple.h", - "source/fuzz/pass_management/repeated_pass_recommender.cpp", - "source/fuzz/pass_management/repeated_pass_recommender.h", - "source/fuzz/pass_management/repeated_pass_recommender_standard.cpp", - "source/fuzz/pass_management/repeated_pass_recommender_standard.h", - "source/fuzz/protobufs/spirvfuzz_protobufs.h", - "source/fuzz/pseudo_random_generator.cpp", - "source/fuzz/pseudo_random_generator.h", - "source/fuzz/random_generator.cpp", - "source/fuzz/random_generator.h", - "source/fuzz/replayer.cpp", - "source/fuzz/replayer.h", - "source/fuzz/shrinker.cpp", - "source/fuzz/shrinker.h", - "source/fuzz/transformation.cpp", - "source/fuzz/transformation.h", - "source/fuzz/transformation_access_chain.cpp", - "source/fuzz/transformation_access_chain.h", - "source/fuzz/transformation_add_bit_instruction_synonym.cpp", - "source/fuzz/transformation_add_bit_instruction_synonym.h", - "source/fuzz/transformation_add_constant_boolean.cpp", - "source/fuzz/transformation_add_constant_boolean.h", - "source/fuzz/transformation_add_constant_composite.cpp", - "source/fuzz/transformation_add_constant_composite.h", - "source/fuzz/transformation_add_constant_null.cpp", - "source/fuzz/transformation_add_constant_null.h", - "source/fuzz/transformation_add_constant_scalar.cpp", - "source/fuzz/transformation_add_constant_scalar.h", - "source/fuzz/transformation_add_copy_memory.cpp", - "source/fuzz/transformation_add_copy_memory.h", - "source/fuzz/transformation_add_dead_block.cpp", - "source/fuzz/transformation_add_dead_block.h", - "source/fuzz/transformation_add_dead_break.cpp", - "source/fuzz/transformation_add_dead_break.h", - "source/fuzz/transformation_add_dead_continue.cpp", - "source/fuzz/transformation_add_dead_continue.h", - "source/fuzz/transformation_add_early_terminator_wrapper.cpp", - "source/fuzz/transformation_add_early_terminator_wrapper.h", - "source/fuzz/transformation_add_function.cpp", - "source/fuzz/transformation_add_function.h", - "source/fuzz/transformation_add_global_undef.cpp", - "source/fuzz/transformation_add_global_undef.h", - "source/fuzz/transformation_add_global_variable.cpp", - "source/fuzz/transformation_add_global_variable.h", - "source/fuzz/transformation_add_image_sample_unused_components.cpp", - "source/fuzz/transformation_add_image_sample_unused_components.h", - "source/fuzz/transformation_add_local_variable.cpp", - "source/fuzz/transformation_add_local_variable.h", - "source/fuzz/transformation_add_loop_preheader.cpp", - "source/fuzz/transformation_add_loop_preheader.h", - "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp", - "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h", - "source/fuzz/transformation_add_no_contraction_decoration.cpp", - "source/fuzz/transformation_add_no_contraction_decoration.h", - "source/fuzz/transformation_add_opphi_synonym.cpp", - "source/fuzz/transformation_add_opphi_synonym.h", - "source/fuzz/transformation_add_parameter.cpp", - "source/fuzz/transformation_add_parameter.h", - "source/fuzz/transformation_add_relaxed_decoration.cpp", - "source/fuzz/transformation_add_relaxed_decoration.h", - "source/fuzz/transformation_add_spec_constant_op.cpp", - "source/fuzz/transformation_add_spec_constant_op.h", - "source/fuzz/transformation_add_synonym.cpp", - "source/fuzz/transformation_add_synonym.h", - "source/fuzz/transformation_add_type_array.cpp", - "source/fuzz/transformation_add_type_array.h", - "source/fuzz/transformation_add_type_boolean.cpp", - "source/fuzz/transformation_add_type_boolean.h", - "source/fuzz/transformation_add_type_float.cpp", - "source/fuzz/transformation_add_type_float.h", - "source/fuzz/transformation_add_type_function.cpp", - "source/fuzz/transformation_add_type_function.h", - "source/fuzz/transformation_add_type_int.cpp", - "source/fuzz/transformation_add_type_int.h", - "source/fuzz/transformation_add_type_matrix.cpp", - "source/fuzz/transformation_add_type_matrix.h", - "source/fuzz/transformation_add_type_pointer.cpp", - "source/fuzz/transformation_add_type_pointer.h", - "source/fuzz/transformation_add_type_struct.cpp", - "source/fuzz/transformation_add_type_struct.h", - "source/fuzz/transformation_add_type_vector.cpp", - "source/fuzz/transformation_add_type_vector.h", - "source/fuzz/transformation_adjust_branch_weights.cpp", - "source/fuzz/transformation_adjust_branch_weights.h", - "source/fuzz/transformation_composite_construct.cpp", - "source/fuzz/transformation_composite_construct.h", - "source/fuzz/transformation_composite_extract.cpp", - "source/fuzz/transformation_composite_extract.h", - "source/fuzz/transformation_composite_insert.cpp", - "source/fuzz/transformation_composite_insert.h", - "source/fuzz/transformation_compute_data_synonym_fact_closure.cpp", - "source/fuzz/transformation_compute_data_synonym_fact_closure.h", - "source/fuzz/transformation_context.cpp", - "source/fuzz/transformation_context.h", - "source/fuzz/transformation_duplicate_region_with_selection.cpp", - "source/fuzz/transformation_duplicate_region_with_selection.h", - "source/fuzz/transformation_equation_instruction.cpp", - "source/fuzz/transformation_equation_instruction.h", - "source/fuzz/transformation_expand_vector_reduction.cpp", - "source/fuzz/transformation_expand_vector_reduction.h", - "source/fuzz/transformation_flatten_conditional_branch.cpp", - "source/fuzz/transformation_flatten_conditional_branch.h", - "source/fuzz/transformation_function_call.cpp", - "source/fuzz/transformation_function_call.h", - "source/fuzz/transformation_inline_function.cpp", - "source/fuzz/transformation_inline_function.h", - "source/fuzz/transformation_invert_comparison_operator.cpp", - "source/fuzz/transformation_invert_comparison_operator.h", - "source/fuzz/transformation_load.cpp", - "source/fuzz/transformation_load.h", - "source/fuzz/transformation_make_vector_operation_dynamic.cpp", - "source/fuzz/transformation_make_vector_operation_dynamic.h", - "source/fuzz/transformation_merge_blocks.cpp", - "source/fuzz/transformation_merge_blocks.h", - "source/fuzz/transformation_merge_function_returns.cpp", - "source/fuzz/transformation_merge_function_returns.h", - "source/fuzz/transformation_move_block_down.cpp", - "source/fuzz/transformation_move_block_down.h", - "source/fuzz/transformation_move_instruction_down.cpp", - "source/fuzz/transformation_move_instruction_down.h", - "source/fuzz/transformation_mutate_pointer.cpp", - "source/fuzz/transformation_mutate_pointer.h", - "source/fuzz/transformation_outline_function.cpp", - "source/fuzz/transformation_outline_function.h", - "source/fuzz/transformation_permute_function_parameters.cpp", - "source/fuzz/transformation_permute_function_parameters.h", - "source/fuzz/transformation_permute_phi_operands.cpp", - "source/fuzz/transformation_permute_phi_operands.h", - "source/fuzz/transformation_propagate_instruction_down.cpp", - "source/fuzz/transformation_propagate_instruction_down.h", - "source/fuzz/transformation_propagate_instruction_up.cpp", - "source/fuzz/transformation_propagate_instruction_up.h", - "source/fuzz/transformation_push_id_through_variable.cpp", - "source/fuzz/transformation_push_id_through_variable.h", - "source/fuzz/transformation_record_synonymous_constants.cpp", - "source/fuzz/transformation_record_synonymous_constants.h", - "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp", - "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h", - "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp", - "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h", - "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp", - "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h", - "source/fuzz/transformation_replace_constant_with_uniform.cpp", - "source/fuzz/transformation_replace_constant_with_uniform.h", - "source/fuzz/transformation_replace_copy_memory_with_load_store.cpp", - "source/fuzz/transformation_replace_copy_memory_with_load_store.h", - "source/fuzz/transformation_replace_copy_object_with_store_load.cpp", - "source/fuzz/transformation_replace_copy_object_with_store_load.h", - "source/fuzz/transformation_replace_id_with_synonym.cpp", - "source/fuzz/transformation_replace_id_with_synonym.h", - "source/fuzz/transformation_replace_irrelevant_id.cpp", - "source/fuzz/transformation_replace_irrelevant_id.h", - "source/fuzz/transformation_replace_linear_algebra_instruction.cpp", - "source/fuzz/transformation_replace_linear_algebra_instruction.h", - "source/fuzz/transformation_replace_load_store_with_copy_memory.cpp", - "source/fuzz/transformation_replace_load_store_with_copy_memory.h", - "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp", - "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h", - "source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp", - "source/fuzz/transformation_replace_opselect_with_conditional_branch.h", - "source/fuzz/transformation_replace_parameter_with_global.cpp", - "source/fuzz/transformation_replace_parameter_with_global.h", - "source/fuzz/transformation_replace_params_with_struct.cpp", - "source/fuzz/transformation_replace_params_with_struct.h", - "source/fuzz/transformation_set_function_control.cpp", - "source/fuzz/transformation_set_function_control.h", - "source/fuzz/transformation_set_loop_control.cpp", - "source/fuzz/transformation_set_loop_control.h", - "source/fuzz/transformation_set_memory_operands_mask.cpp", - "source/fuzz/transformation_set_memory_operands_mask.h", - "source/fuzz/transformation_set_selection_control.cpp", - "source/fuzz/transformation_set_selection_control.h", - "source/fuzz/transformation_split_block.cpp", - "source/fuzz/transformation_split_block.h", - "source/fuzz/transformation_store.cpp", - "source/fuzz/transformation_store.h", - "source/fuzz/transformation_swap_commutable_operands.cpp", - "source/fuzz/transformation_swap_commutable_operands.h", - "source/fuzz/transformation_swap_conditional_branch_operands.cpp", - "source/fuzz/transformation_swap_conditional_branch_operands.h", - "source/fuzz/transformation_swap_function_variables.cpp", - "source/fuzz/transformation_swap_function_variables.h", - "source/fuzz/transformation_swap_two_functions.cpp", - "source/fuzz/transformation_swap_two_functions.h", - "source/fuzz/transformation_toggle_access_chain_instruction.cpp", - "source/fuzz/transformation_toggle_access_chain_instruction.h", - "source/fuzz/transformation_vector_shuffle.cpp", - "source/fuzz/transformation_vector_shuffle.h", - "source/fuzz/transformation_wrap_early_terminator_in_function.cpp", - "source/fuzz/transformation_wrap_early_terminator_in_function.h", - "source/fuzz/transformation_wrap_region_in_selection.cpp", - "source/fuzz/transformation_wrap_region_in_selection.h", - "source/fuzz/uniform_buffer_element_descriptor.cpp", - "source/fuzz/uniform_buffer_element_descriptor.h", - ] - deps = [ - "//third_party/protobuf:protobuf_full", - ":spvtools_fuzz_proto", - ":spvtools", - ":spvtools_opt", - ] - public_deps = [ ":spvtools_headers" ] - if (build_with_chromium) { + # The spirv-fuzz library is only built when in a Chromium checkout + # due to its dependency on protobuf. + + proto_library("spvtools_fuzz_proto") { + sources = [ + "source/fuzz/protobufs/spvtoolsfuzz.proto", + ] + generate_python = false + use_protobuf_full = true + } + + static_library("spvtools_fuzz") { + sources = [ + "source/fuzz/added_function_reducer.cpp", + "source/fuzz/added_function_reducer.h", + "source/fuzz/available_instructions.cpp", + "source/fuzz/available_instructions.h", + "source/fuzz/call_graph.cpp", + "source/fuzz/call_graph.h", + "source/fuzz/comparator_deep_blocks_first.h", + "source/fuzz/counter_overflow_id_source.cpp", + "source/fuzz/counter_overflow_id_source.h", + "source/fuzz/data_descriptor.cpp", + "source/fuzz/data_descriptor.h", + "source/fuzz/equivalence_relation.h", + "source/fuzz/fact_manager/constant_uniform_facts.cpp", + "source/fuzz/fact_manager/constant_uniform_facts.h", + "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp", + "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h", + "source/fuzz/fact_manager/dead_block_facts.cpp", + "source/fuzz/fact_manager/dead_block_facts.h", + "source/fuzz/fact_manager/fact_manager.cpp", + "source/fuzz/fact_manager/fact_manager.h", + "source/fuzz/fact_manager/irrelevant_value_facts.cpp", + "source/fuzz/fact_manager/irrelevant_value_facts.h", + "source/fuzz/fact_manager/livesafe_function_facts.cpp", + "source/fuzz/fact_manager/livesafe_function_facts.h", + "source/fuzz/force_render_red.cpp", + "source/fuzz/force_render_red.h", + "source/fuzz/fuzzer.cpp", + "source/fuzz/fuzzer.h", + "source/fuzz/fuzzer_context.cpp", + "source/fuzz/fuzzer_context.h", + "source/fuzz/fuzzer_pass.cpp", + "source/fuzz/fuzzer_pass.h", + "source/fuzz/fuzzer_pass_add_access_chains.cpp", + "source/fuzz/fuzzer_pass_add_access_chains.h", + "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h", + "source/fuzz/fuzzer_pass_add_composite_extract.cpp", + "source/fuzz/fuzzer_pass_add_composite_extract.h", + "source/fuzz/fuzzer_pass_add_composite_inserts.cpp", + "source/fuzz/fuzzer_pass_add_composite_inserts.h", + "source/fuzz/fuzzer_pass_add_composite_types.cpp", + "source/fuzz/fuzzer_pass_add_composite_types.h", + "source/fuzz/fuzzer_pass_add_copy_memory.cpp", + "source/fuzz/fuzzer_pass_add_copy_memory.h", + "source/fuzz/fuzzer_pass_add_dead_blocks.cpp", + "source/fuzz/fuzzer_pass_add_dead_blocks.h", + "source/fuzz/fuzzer_pass_add_dead_breaks.cpp", + "source/fuzz/fuzzer_pass_add_dead_breaks.h", + "source/fuzz/fuzzer_pass_add_dead_continues.cpp", + "source/fuzz/fuzzer_pass_add_dead_continues.h", + "source/fuzz/fuzzer_pass_add_equation_instructions.cpp", + "source/fuzz/fuzzer_pass_add_equation_instructions.h", + "source/fuzz/fuzzer_pass_add_function_calls.cpp", + "source/fuzz/fuzzer_pass_add_function_calls.h", + "source/fuzz/fuzzer_pass_add_global_variables.cpp", + "source/fuzz/fuzzer_pass_add_global_variables.h", + "source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp", + "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h", + "source/fuzz/fuzzer_pass_add_loads.cpp", + "source/fuzz/fuzzer_pass_add_loads.h", + "source/fuzz/fuzzer_pass_add_local_variables.cpp", + "source/fuzz/fuzzer_pass_add_local_variables.h", + "source/fuzz/fuzzer_pass_add_loop_preheaders.cpp", + "source/fuzz/fuzzer_pass_add_loop_preheaders.h", + "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h", + "source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp", + "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h", + "source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_opphi_synonyms.h", + "source/fuzz/fuzzer_pass_add_parameters.cpp", + "source/fuzz/fuzzer_pass_add_parameters.h", + "source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp", + "source/fuzz/fuzzer_pass_add_relaxed_decorations.h", + "source/fuzz/fuzzer_pass_add_stores.cpp", + "source/fuzz/fuzzer_pass_add_stores.h", + "source/fuzz/fuzzer_pass_add_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_synonyms.h", + "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp", + "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h", + "source/fuzz/fuzzer_pass_adjust_branch_weights.cpp", + "source/fuzz/fuzzer_pass_adjust_branch_weights.h", + "source/fuzz/fuzzer_pass_adjust_function_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_function_controls.h", + "source/fuzz/fuzzer_pass_adjust_loop_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_loop_controls.h", + "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp", + "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h", + "source/fuzz/fuzzer_pass_adjust_selection_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_selection_controls.h", + "source/fuzz/fuzzer_pass_apply_id_synonyms.cpp", + "source/fuzz/fuzzer_pass_apply_id_synonyms.h", + "source/fuzz/fuzzer_pass_construct_composites.cpp", + "source/fuzz/fuzzer_pass_construct_composites.h", + "source/fuzz/fuzzer_pass_copy_objects.cpp", + "source/fuzz/fuzzer_pass_copy_objects.h", + "source/fuzz/fuzzer_pass_donate_modules.cpp", + "source/fuzz/fuzzer_pass_donate_modules.h", + "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp", + "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h", + "source/fuzz/fuzzer_pass_expand_vector_reductions.cpp", + "source/fuzz/fuzzer_pass_expand_vector_reductions.h", + "source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp", + "source/fuzz/fuzzer_pass_flatten_conditional_branches.h", + "source/fuzz/fuzzer_pass_inline_functions.cpp", + "source/fuzz/fuzzer_pass_inline_functions.h", + "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp", + "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h", + "source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp", + "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h", + "source/fuzz/fuzzer_pass_invert_comparison_operators.cpp", + "source/fuzz/fuzzer_pass_invert_comparison_operators.h", + "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp", + "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h", + "source/fuzz/fuzzer_pass_merge_blocks.cpp", + "source/fuzz/fuzzer_pass_merge_blocks.h", + "source/fuzz/fuzzer_pass_merge_function_returns.cpp", + "source/fuzz/fuzzer_pass_merge_function_returns.h", + "source/fuzz/fuzzer_pass_mutate_pointers.cpp", + "source/fuzz/fuzzer_pass_mutate_pointers.h", + "source/fuzz/fuzzer_pass_obfuscate_constants.cpp", + "source/fuzz/fuzzer_pass_obfuscate_constants.h", + "source/fuzz/fuzzer_pass_outline_functions.cpp", + "source/fuzz/fuzzer_pass_outline_functions.h", + "source/fuzz/fuzzer_pass_permute_blocks.cpp", + "source/fuzz/fuzzer_pass_permute_blocks.h", + "source/fuzz/fuzzer_pass_permute_function_parameters.cpp", + "source/fuzz/fuzzer_pass_permute_function_parameters.h", + "source/fuzz/fuzzer_pass_permute_function_variables.cpp", + "source/fuzz/fuzzer_pass_permute_function_variables.h", + "source/fuzz/fuzzer_pass_permute_instructions.cpp", + "source/fuzz/fuzzer_pass_permute_instructions.h", + "source/fuzz/fuzzer_pass_permute_phi_operands.cpp", + "source/fuzz/fuzzer_pass_permute_phi_operands.h", + "source/fuzz/fuzzer_pass_propagate_instructions_down.cpp", + "source/fuzz/fuzzer_pass_propagate_instructions_down.h", + "source/fuzz/fuzzer_pass_propagate_instructions_up.cpp", + "source/fuzz/fuzzer_pass_propagate_instructions_up.h", + "source/fuzz/fuzzer_pass_push_ids_through_variables.cpp", + "source/fuzz/fuzzer_pass_push_ids_through_variables.h", + "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp", + "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h", + "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp", + "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h", + "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp", + "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h", + "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp", + "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h", + "source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp", + "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h", + "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp", + "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h", + "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp", + "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h", + "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp", + "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h", + "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp", + "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h", + "source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp", + "source/fuzz/fuzzer_pass_replace_parameter_with_global.h", + "source/fuzz/fuzzer_pass_replace_params_with_struct.cpp", + "source/fuzz/fuzzer_pass_replace_params_with_struct.h", + "source/fuzz/fuzzer_pass_split_blocks.cpp", + "source/fuzz/fuzzer_pass_split_blocks.h", + "source/fuzz/fuzzer_pass_swap_commutable_operands.cpp", + "source/fuzz/fuzzer_pass_swap_commutable_operands.h", + "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp", + "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h", + "source/fuzz/fuzzer_pass_swap_functions.cpp", + "source/fuzz/fuzzer_pass_swap_functions.h", + "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp", + "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h", + "source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp", + "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h", + "source/fuzz/fuzzer_util.cpp", + "source/fuzz/fuzzer_util.h", + "source/fuzz/id_use_descriptor.cpp", + "source/fuzz/id_use_descriptor.h", + "source/fuzz/instruction_descriptor.cpp", + "source/fuzz/instruction_descriptor.h", + "source/fuzz/instruction_message.cpp", + "source/fuzz/instruction_message.h", + "source/fuzz/overflow_id_source.cpp", + "source/fuzz/overflow_id_source.h", + "source/fuzz/pass_management/repeated_pass_instances.h", + "source/fuzz/pass_management/repeated_pass_manager.cpp", + "source/fuzz/pass_management/repeated_pass_manager.h", + "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp", + "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h", + "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp", + "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h", + "source/fuzz/pass_management/repeated_pass_manager_simple.cpp", + "source/fuzz/pass_management/repeated_pass_manager_simple.h", + "source/fuzz/pass_management/repeated_pass_recommender.cpp", + "source/fuzz/pass_management/repeated_pass_recommender.h", + "source/fuzz/pass_management/repeated_pass_recommender_standard.cpp", + "source/fuzz/pass_management/repeated_pass_recommender_standard.h", + "source/fuzz/protobufs/spirvfuzz_protobufs.h", + "source/fuzz/pseudo_random_generator.cpp", + "source/fuzz/pseudo_random_generator.h", + "source/fuzz/random_generator.cpp", + "source/fuzz/random_generator.h", + "source/fuzz/replayer.cpp", + "source/fuzz/replayer.h", + "source/fuzz/shrinker.cpp", + "source/fuzz/shrinker.h", + "source/fuzz/transformation.cpp", + "source/fuzz/transformation.h", + "source/fuzz/transformation_access_chain.cpp", + "source/fuzz/transformation_access_chain.h", + "source/fuzz/transformation_add_bit_instruction_synonym.cpp", + "source/fuzz/transformation_add_bit_instruction_synonym.h", + "source/fuzz/transformation_add_constant_boolean.cpp", + "source/fuzz/transformation_add_constant_boolean.h", + "source/fuzz/transformation_add_constant_composite.cpp", + "source/fuzz/transformation_add_constant_composite.h", + "source/fuzz/transformation_add_constant_null.cpp", + "source/fuzz/transformation_add_constant_null.h", + "source/fuzz/transformation_add_constant_scalar.cpp", + "source/fuzz/transformation_add_constant_scalar.h", + "source/fuzz/transformation_add_copy_memory.cpp", + "source/fuzz/transformation_add_copy_memory.h", + "source/fuzz/transformation_add_dead_block.cpp", + "source/fuzz/transformation_add_dead_block.h", + "source/fuzz/transformation_add_dead_break.cpp", + "source/fuzz/transformation_add_dead_break.h", + "source/fuzz/transformation_add_dead_continue.cpp", + "source/fuzz/transformation_add_dead_continue.h", + "source/fuzz/transformation_add_early_terminator_wrapper.cpp", + "source/fuzz/transformation_add_early_terminator_wrapper.h", + "source/fuzz/transformation_add_function.cpp", + "source/fuzz/transformation_add_function.h", + "source/fuzz/transformation_add_global_undef.cpp", + "source/fuzz/transformation_add_global_undef.h", + "source/fuzz/transformation_add_global_variable.cpp", + "source/fuzz/transformation_add_global_variable.h", + "source/fuzz/transformation_add_image_sample_unused_components.cpp", + "source/fuzz/transformation_add_image_sample_unused_components.h", + "source/fuzz/transformation_add_local_variable.cpp", + "source/fuzz/transformation_add_local_variable.h", + "source/fuzz/transformation_add_loop_preheader.cpp", + "source/fuzz/transformation_add_loop_preheader.h", + "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp", + "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h", + "source/fuzz/transformation_add_no_contraction_decoration.cpp", + "source/fuzz/transformation_add_no_contraction_decoration.h", + "source/fuzz/transformation_add_opphi_synonym.cpp", + "source/fuzz/transformation_add_opphi_synonym.h", + "source/fuzz/transformation_add_parameter.cpp", + "source/fuzz/transformation_add_parameter.h", + "source/fuzz/transformation_add_relaxed_decoration.cpp", + "source/fuzz/transformation_add_relaxed_decoration.h", + "source/fuzz/transformation_add_spec_constant_op.cpp", + "source/fuzz/transformation_add_spec_constant_op.h", + "source/fuzz/transformation_add_synonym.cpp", + "source/fuzz/transformation_add_synonym.h", + "source/fuzz/transformation_add_type_array.cpp", + "source/fuzz/transformation_add_type_array.h", + "source/fuzz/transformation_add_type_boolean.cpp", + "source/fuzz/transformation_add_type_boolean.h", + "source/fuzz/transformation_add_type_float.cpp", + "source/fuzz/transformation_add_type_float.h", + "source/fuzz/transformation_add_type_function.cpp", + "source/fuzz/transformation_add_type_function.h", + "source/fuzz/transformation_add_type_int.cpp", + "source/fuzz/transformation_add_type_int.h", + "source/fuzz/transformation_add_type_matrix.cpp", + "source/fuzz/transformation_add_type_matrix.h", + "source/fuzz/transformation_add_type_pointer.cpp", + "source/fuzz/transformation_add_type_pointer.h", + "source/fuzz/transformation_add_type_struct.cpp", + "source/fuzz/transformation_add_type_struct.h", + "source/fuzz/transformation_add_type_vector.cpp", + "source/fuzz/transformation_add_type_vector.h", + "source/fuzz/transformation_adjust_branch_weights.cpp", + "source/fuzz/transformation_adjust_branch_weights.h", + "source/fuzz/transformation_composite_construct.cpp", + "source/fuzz/transformation_composite_construct.h", + "source/fuzz/transformation_composite_extract.cpp", + "source/fuzz/transformation_composite_extract.h", + "source/fuzz/transformation_composite_insert.cpp", + "source/fuzz/transformation_composite_insert.h", + "source/fuzz/transformation_compute_data_synonym_fact_closure.cpp", + "source/fuzz/transformation_compute_data_synonym_fact_closure.h", + "source/fuzz/transformation_context.cpp", + "source/fuzz/transformation_context.h", + "source/fuzz/transformation_duplicate_region_with_selection.cpp", + "source/fuzz/transformation_duplicate_region_with_selection.h", + "source/fuzz/transformation_equation_instruction.cpp", + "source/fuzz/transformation_equation_instruction.h", + "source/fuzz/transformation_expand_vector_reduction.cpp", + "source/fuzz/transformation_expand_vector_reduction.h", + "source/fuzz/transformation_flatten_conditional_branch.cpp", + "source/fuzz/transformation_flatten_conditional_branch.h", + "source/fuzz/transformation_function_call.cpp", + "source/fuzz/transformation_function_call.h", + "source/fuzz/transformation_inline_function.cpp", + "source/fuzz/transformation_inline_function.h", + "source/fuzz/transformation_invert_comparison_operator.cpp", + "source/fuzz/transformation_invert_comparison_operator.h", + "source/fuzz/transformation_load.cpp", + "source/fuzz/transformation_load.h", + "source/fuzz/transformation_make_vector_operation_dynamic.cpp", + "source/fuzz/transformation_make_vector_operation_dynamic.h", + "source/fuzz/transformation_merge_blocks.cpp", + "source/fuzz/transformation_merge_blocks.h", + "source/fuzz/transformation_merge_function_returns.cpp", + "source/fuzz/transformation_merge_function_returns.h", + "source/fuzz/transformation_move_block_down.cpp", + "source/fuzz/transformation_move_block_down.h", + "source/fuzz/transformation_move_instruction_down.cpp", + "source/fuzz/transformation_move_instruction_down.h", + "source/fuzz/transformation_mutate_pointer.cpp", + "source/fuzz/transformation_mutate_pointer.h", + "source/fuzz/transformation_outline_function.cpp", + "source/fuzz/transformation_outline_function.h", + "source/fuzz/transformation_permute_function_parameters.cpp", + "source/fuzz/transformation_permute_function_parameters.h", + "source/fuzz/transformation_permute_phi_operands.cpp", + "source/fuzz/transformation_permute_phi_operands.h", + "source/fuzz/transformation_propagate_instruction_down.cpp", + "source/fuzz/transformation_propagate_instruction_down.h", + "source/fuzz/transformation_propagate_instruction_up.cpp", + "source/fuzz/transformation_propagate_instruction_up.h", + "source/fuzz/transformation_push_id_through_variable.cpp", + "source/fuzz/transformation_push_id_through_variable.h", + "source/fuzz/transformation_record_synonymous_constants.cpp", + "source/fuzz/transformation_record_synonymous_constants.h", + "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp", + "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h", + "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp", + "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h", + "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp", + "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h", + "source/fuzz/transformation_replace_constant_with_uniform.cpp", + "source/fuzz/transformation_replace_constant_with_uniform.h", + "source/fuzz/transformation_replace_copy_memory_with_load_store.cpp", + "source/fuzz/transformation_replace_copy_memory_with_load_store.h", + "source/fuzz/transformation_replace_copy_object_with_store_load.cpp", + "source/fuzz/transformation_replace_copy_object_with_store_load.h", + "source/fuzz/transformation_replace_id_with_synonym.cpp", + "source/fuzz/transformation_replace_id_with_synonym.h", + "source/fuzz/transformation_replace_irrelevant_id.cpp", + "source/fuzz/transformation_replace_irrelevant_id.h", + "source/fuzz/transformation_replace_linear_algebra_instruction.cpp", + "source/fuzz/transformation_replace_linear_algebra_instruction.h", + "source/fuzz/transformation_replace_load_store_with_copy_memory.cpp", + "source/fuzz/transformation_replace_load_store_with_copy_memory.h", + "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp", + "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h", + "source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp", + "source/fuzz/transformation_replace_opselect_with_conditional_branch.h", + "source/fuzz/transformation_replace_parameter_with_global.cpp", + "source/fuzz/transformation_replace_parameter_with_global.h", + "source/fuzz/transformation_replace_params_with_struct.cpp", + "source/fuzz/transformation_replace_params_with_struct.h", + "source/fuzz/transformation_set_function_control.cpp", + "source/fuzz/transformation_set_function_control.h", + "source/fuzz/transformation_set_loop_control.cpp", + "source/fuzz/transformation_set_loop_control.h", + "source/fuzz/transformation_set_memory_operands_mask.cpp", + "source/fuzz/transformation_set_memory_operands_mask.h", + "source/fuzz/transformation_set_selection_control.cpp", + "source/fuzz/transformation_set_selection_control.h", + "source/fuzz/transformation_split_block.cpp", + "source/fuzz/transformation_split_block.h", + "source/fuzz/transformation_store.cpp", + "source/fuzz/transformation_store.h", + "source/fuzz/transformation_swap_commutable_operands.cpp", + "source/fuzz/transformation_swap_commutable_operands.h", + "source/fuzz/transformation_swap_conditional_branch_operands.cpp", + "source/fuzz/transformation_swap_conditional_branch_operands.h", + "source/fuzz/transformation_swap_function_variables.cpp", + "source/fuzz/transformation_swap_function_variables.h", + "source/fuzz/transformation_swap_two_functions.cpp", + "source/fuzz/transformation_swap_two_functions.h", + "source/fuzz/transformation_toggle_access_chain_instruction.cpp", + "source/fuzz/transformation_toggle_access_chain_instruction.h", + "source/fuzz/transformation_vector_shuffle.cpp", + "source/fuzz/transformation_vector_shuffle.h", + "source/fuzz/transformation_wrap_early_terminator_in_function.cpp", + "source/fuzz/transformation_wrap_early_terminator_in_function.h", + "source/fuzz/transformation_wrap_region_in_selection.cpp", + "source/fuzz/transformation_wrap_region_in_selection.h", + "source/fuzz/uniform_buffer_element_descriptor.cpp", + "source/fuzz/uniform_buffer_element_descriptor.h", + ] + deps = [ + "//third_party/protobuf:protobuf_full", + ":spvtools_fuzz_proto", + ":spvtools", + ":spvtools_opt", + ] + public_deps = [ ":spvtools_headers" ] configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] + configs += [ ":spvtools_internal_config" ] } - configs += [ ":spvtools_internal_config" ] } group("SPIRV-Tools") { @@ -1476,10 +1480,11 @@ executable("spirv-link") { configs += [ ":spvtools_internal_config" ] } -if (!is_ios && !spirv_is_winuwp) { +if (!is_ios && !spirv_is_winuwp && build_with_chromium) { - # iOS and UWP do not allow std::system calls which spirv-fuzz and - # spirv-reduce require + # iOS and UWP do not allow std::system calls which spirv-fuzz + # requires. Additionally, spirv-fuzz is only built when in a + # Chromium checkout due to its dependency on protobuf. executable("spirv-fuzz") { sources = [ "tools/fuzz/fuzz.cpp" ] @@ -1495,6 +1500,12 @@ if (!is_ios && !spirv_is_winuwp) { ] configs += [ ":spvtools_internal_config" ] } +} + +if (!is_ios && !spirv_is_winuwp) { + + # iOS and UWP do not allow std::system calls which spirv-reduce + # requires. executable("spirv-reduce") { sources = [ "tools/reduce/reduce.cpp" ] @@ -1519,10 +1530,10 @@ group("all_spirv_tools") { ":spirv-opt", ":spirv-val", ] + if (!is_ios && !spirv_is_winuwp && build_with_chromium) { + deps += [ ":spirv-fuzz" ] + } if (!is_ios && !spirv_is_winuwp) { - deps += [ - ":spirv-fuzz", - ":spirv-reduce", - ] + deps += [ ":spirv-reduce" ] } } -- cgit v1.2.3 From 4baf3affe32392b26b2b3394d9e79095d7c6d99a Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Wed, 14 Jul 2021 08:43:35 -0400 Subject: spirv-opt: support SPV_EXT_shader_image_int64 (#4379) --- 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 + source/opt/local_single_store_elim_pass.cpp | 1 + 4 files changed, 4 insertions(+) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 7cffff57..8c248b39 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -999,6 +999,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_KHR_vulkan_memory_model", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", }); } diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index 5c10ecec..5485c038 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -420,6 +420,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_KHR_terminate_invocation", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", }); } diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index 05ed28ae..a964c4c6 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -272,6 +272,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_KHR_terminate_invocation", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", }); } diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index 7eb4b1fd..f3864a8d 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -125,6 +125,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_KHR_terminate_invocation", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { -- cgit v1.2.3 From 640b17b5fbc65f2aed9106ad96ba40c51fa20195 Mon Sep 17 00:00:00 2001 From: pkasting Date: Wed, 14 Jul 2021 07:23:59 -0700 Subject: Fix -Wunreachable-code-aggressive. (#4358) Bug: chromium:1066980 --- tools/cfg/cfg.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp index a93488d5..6a5faa18 100644 --- a/tools/cfg/cfg.cpp +++ b/tools/cfg/cfg.cpp @@ -69,16 +69,16 @@ int main(int argc, char** argv) { if (0 == strcmp(argv[argi], "--help")) { print_usage(argv[0]); return 0; - } else if (0 == strcmp(argv[argi], "--version")) { + } + if (0 == strcmp(argv[argi], "--version")) { printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString()); printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment)); return 0; - } else { - print_usage(argv[0]); - return 1; } - } break; + print_usage(argv[0]); + return 1; + } case 0: { // Setting a filename of "-" to indicate stdin. if (!inFile) { -- cgit v1.2.3 From 2685c9a687b5d8a62b9598dc2b2671e5e19e7ecf Mon Sep 17 00:00:00 2001 From: Jamie Madill Date: Thu, 15 Jul 2021 12:56:13 -0400 Subject: Add missing fuzzer header dependency. (#4381) Should fix the failing GN header check where the reduce header was not visible to the fuzzer target. Also reformats the GN file using 'gn format'. --- BUILD.gn | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 53e2128b..fb6d03fc 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -232,8 +232,7 @@ template("spvtools_vendor_table_local") { script = "utils/generate_grammar_tables.py" name = invoker.name - extinst_vendor_grammar = - "source/extinst.${name}.grammar.json" + extinst_vendor_grammar = "source/extinst.${name}.grammar.json" extinst_file = "${target_gen_dir}/${name}.insts.inc" args = [ @@ -336,12 +335,10 @@ spvtools_vendor_tables = [ ], ] -spvtools_vendor_tables_local = [ - [ - "nonsemantic.vulkan.debuginfo.100", - "VKDEBUG100_", - ], -] +spvtools_vendor_tables_local = [ [ + "nonsemantic.vulkan.debuginfo.100", + "VKDEBUG100_", + ] ] foreach(table_def, spvtools_vendor_tables) { spvtools_vendor_table(table_def[0]) { @@ -400,8 +397,8 @@ static_library("spvtools") { ":spvtools_generators_inc", ":spvtools_glsl_tables_glsl1-0", ":spvtools_language_header_cldebuginfo100", - ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_opencl_tables_opencl1-0", ] foreach(table_def, spvtools_vendor_tables) { @@ -550,8 +547,8 @@ static_library("spvtools_val") { deps = [ ":spvtools", ":spvtools_language_header_cldebuginfo100", - ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", ] public_deps = [ ":spvtools_headers" ] @@ -775,8 +772,8 @@ static_library("spvtools_opt") { deps = [ ":spvtools", ":spvtools_language_header_cldebuginfo100", - ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_vendor_tables_spv-amd-shader-ballot", ] public_deps = [ ":spvtools_headers" ] @@ -879,14 +876,11 @@ static_library("spvtools_reduce") { } if (build_with_chromium) { - # The spirv-fuzz library is only built when in a Chromium checkout # due to its dependency on protobuf. proto_library("spvtools_fuzz_proto") { - sources = [ - "source/fuzz/protobufs/spvtoolsfuzz.proto", - ] + sources = [ "source/fuzz/protobufs/spvtoolsfuzz.proto" ] generate_python = false use_protobuf_full = true } @@ -1281,10 +1275,11 @@ if (build_with_chromium) { "source/fuzz/uniform_buffer_element_descriptor.h", ] deps = [ - "//third_party/protobuf:protobuf_full", - ":spvtools_fuzz_proto", ":spvtools", + ":spvtools_fuzz_proto", ":spvtools_opt", + ":spvtools_reduce", + "//third_party/protobuf:protobuf_full", ] public_deps = [ ":spvtools_headers" ] configs -= [ "//build/config/compiler:chromium_code" ] @@ -1371,8 +1366,8 @@ if (build_with_chromium) { deps = [ ":spvtools", ":spvtools_language_header_cldebuginfo100", - ":spvtools_language_header_vkdebuginfo100", ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_val", "//testing/gmock", "//testing/gtest", @@ -1481,7 +1476,6 @@ executable("spirv-link") { } if (!is_ios && !spirv_is_winuwp && build_with_chromium) { - # iOS and UWP do not allow std::system calls which spirv-fuzz # requires. Additionally, spirv-fuzz is only built when in a # Chromium checkout due to its dependency on protobuf. @@ -1489,21 +1483,20 @@ if (!is_ios && !spirv_is_winuwp && build_with_chromium) { executable("spirv-fuzz") { sources = [ "tools/fuzz/fuzz.cpp" ] deps = [ - "//third_party/protobuf:protobuf_full", ":spvtools", ":spvtools_fuzz", - ":spvtools_reduce", ":spvtools_opt", + ":spvtools_reduce", ":spvtools_software_version", ":spvtools_util_cli_consumer", ":spvtools_val", + "//third_party/protobuf:protobuf_full", ] configs += [ ":spvtools_internal_config" ] } } if (!is_ios && !spirv_is_winuwp) { - # iOS and UWP do not allow std::system calls which spirv-reduce # requires. -- cgit v1.2.3 From e0937d7fd1dbcb306c93d0806954a0dac08f48b9 Mon Sep 17 00:00:00 2001 From: Mostafa Ashraf Date: Thu, 15 Jul 2021 20:03:51 +0200 Subject: spirv-fuzz: Don't replace memory semantics / scope operands (#4349) This change is responsible for avoiding the replacement of constant operands with another one not constant, in the context of atomic operations. The related rule from the SPIR-V spec is: "All used for Scope and Memory Semantics in shader capability must be of an OpConstant." Fixes #4346. --- source/fuzz/fuzzer_util.cpp | 38 +++++ ...transformation_replace_id_with_synonym_test.cpp | 186 ++++++++++++++++++--- 2 files changed, 202 insertions(+), 22 deletions(-) diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 4beb67fd..eaa62255 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -1616,6 +1616,44 @@ bool IdUseCanBeReplaced(opt::IRContext* ir_context, return false; } + if (ir_context->get_feature_mgr()->HasCapability(SpvCapabilityShader)) { + // With the Shader capability, memory scope and memory semantics operands + // are required to be constants, so they cannot be replaced arbitrarily. + switch (use_instruction->opcode()) { + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + if (use_in_operand_index == 1 || use_in_operand_index == 2) { + return false; + } + break; + case SpvOpAtomicCompareExchange: + if (use_in_operand_index == 1 || use_in_operand_index == 2 || + use_in_operand_index == 3) { + return false; + } + break; + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicFlagTestAndSet: + case SpvOpAtomicFlagClear: + case SpvOpAtomicFAddEXT: + assert(false && "Not allowed with the Shader capability."); + default: + break; + } + } + return true; } diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index c0739c3b..5442f804 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -1796,27 +1796,8 @@ TEST(TransformationReplaceIdWithSynonymTest, IncompatibleTypes) { .IsApplicable(context.get(), transformation_context)); } -// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4346): This test -// should be updated to cover more atomic operations, and once the issue is -// fixed the test should be enabled. TEST(TransformationReplaceIdWithSynonymTest, - DISABLED_AtomicScopeAndMemorySemanticsMustBeConstant) { - // The following SPIR-V came from this GLSL, edited to add some synonyms: - // - // #version 320 es - // - // #extension GL_KHR_memory_scope_semantics : enable - // - // layout(set = 0, binding = 0) buffer Buf { - // int x; - // }; - // - // void main() { - // int tmp = atomicLoad(x, - // gl_ScopeWorkgroup, - // gl_StorageSemanticsBuffer, - // gl_SemanticsRelaxed); - // } + AtomicScopeAndMemorySemanticsMustBeConstant) { const std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -1832,15 +1813,19 @@ TEST(TransformationReplaceIdWithSynonymTest, %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 + %17 = OpTypeInt 32 0 %7 = OpTypePointer Function %6 %9 = OpTypeStruct %6 %10 = OpTypePointer StorageBuffer %9 %11 = OpVariable %10 StorageBuffer + %86 = OpTypeStruct %17 + %87 = OpTypePointer Workgroup %86 + %88 = OpVariable %87 Workgroup %12 = OpConstant %6 0 %13 = OpTypePointer StorageBuffer %6 %15 = OpConstant %6 2 %16 = OpConstant %6 64 - %17 = OpTypeInt 32 0 + %89 = OpTypePointer Workgroup %17 %18 = OpConstant %17 1 %19 = OpConstant %17 0 %20 = OpConstant %17 64 @@ -1850,8 +1835,23 @@ TEST(TransformationReplaceIdWithSynonymTest, %100 = OpCopyObject %6 %15 ; A non-constant version of %15 %101 = OpCopyObject %17 %20 ; A non-constant version of %20 %14 = OpAccessChain %13 %11 %12 + %90 = OpAccessChain %89 %88 %19 %21 = OpAtomicLoad %6 %14 %15 %20 + %22 = OpAtomicExchange %6 %14 %15 %20 %16 + %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 + %24 = OpAtomicIIncrement %6 %14 %15 %20 + %25 = OpAtomicIDecrement %6 %14 %15 %20 + %26 = OpAtomicIAdd %6 %14 %15 %20 %16 + %27 = OpAtomicISub %6 %14 %15 %20 %16 + %28 = OpAtomicSMin %6 %14 %15 %20 %16 + %29 = OpAtomicUMin %17 %90 %15 %20 %18 + %30 = OpAtomicSMax %6 %14 %15 %20 %15 + %31 = OpAtomicUMax %17 %90 %15 %20 %18 + %32 = OpAtomicAnd %6 %14 %15 %20 %16 + %33 = OpAtomicOr %6 %14 %15 %20 %16 + %34 = OpAtomicXor %6 %14 %15 %20 %16 OpStore %8 %21 + OpAtomicStore %14 %15 %20 %12 OpReturn OpFunctionEnd )"; @@ -1872,7 +1872,7 @@ TEST(TransformationReplaceIdWithSynonymTest, // Tell the fact manager that %101 and %20 are synonymous transformation_context.GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {})); - + // OpAtomicLoad const auto& scope_operand = MakeIdUseDescriptorFromUse( context.get(), context->get_def_use_mgr()->GetDef(21), 1); ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand, 100) @@ -1882,6 +1882,148 @@ TEST(TransformationReplaceIdWithSynonymTest, context.get(), context->get_def_use_mgr()->GetDef(21), 2); ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand, 101) .IsApplicable(context.get(), transformation_context)); + // OpAtomicExchange. + const auto& scope_operand2 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(22), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand2, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand2 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(22), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand2, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicCompareExchange. + const auto& scope_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand3, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_equal_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_equal_operand3, 101) + .IsApplicable(context.get(), transformation_context)); + const auto& semantics_unequal_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 3); + ASSERT_FALSE( + TransformationReplaceIdWithSynonym(semantics_unequal_operand3, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicIIncrement. + const auto& scope_operand4 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(24), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand4, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand4 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(24), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand4, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicIDecrement. + const auto& scope_operand5 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(25), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand5, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand5 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(25), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand5, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicIAdd. + const auto& scope_operand6 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(26), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand6, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand6 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(26), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand6, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicISub + const auto& scope_operand8 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(27), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand8, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand8 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(27), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand8, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicSMin + const auto& scope_operand9 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(28), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand9, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand9 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(28), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand9, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicUMin + const auto& scope_operand10 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(29), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand10, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand10 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(29), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand10, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicSMax + const auto& scope_operand11 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(30), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand11, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand11 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(30), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand11, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicUMax + const auto& scope_operand12 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(31), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand12, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand12 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(31), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand12, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicAnd + const auto& scope_operand13 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(32), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand13, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand13 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(32), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand13, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicOr + const auto& scope_operand14 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(33), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand14, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand14 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(33), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand14, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicXor + const auto& scope_operand15 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(34), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand15, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand15 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(34), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand15, 101) + .IsApplicable(context.get(), transformation_context)); } // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this -- cgit v1.2.3 From 7320b9acd251939251500813747e5764f2e3bfd4 Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Fri, 16 Jul 2021 11:48:22 -0400 Subject: Explain how to run tests with CMake and Bazel (#4383) Update README. --- README.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db52b79f..804b7637 100644 --- a/README.md +++ b/README.md @@ -643,8 +643,32 @@ This is experimental. ### Tests -Tests are only built when googletest is found. Use `ctest` to run all the -tests. +Tests are only built when googletest is found. + +#### Running test with CMake + +Use `ctest -j ` to run all the tests. To run tests using all threads: +```shell +ctest -j$(nproc) +``` + +To run a single test target, use `ctest [-j ] -R `. For example, +you can run all `opt` tests with: +```shell +ctest -R 'spirv-tools-test_opt' +``` + +#### Running test with Bazel + +Use `bazel test :all` to run all tests. This will run tests in parallel by default. + +To run a single test target, specify `:my_test_target` instead of `:all`. Test target +names get printed when you run `bazel test :all`. For example, you can run +`opt_def_use_test` with: +```shell +bazel test :opt_def_use_test +``` + ## Future Work -- cgit v1.2.3 From 5427d5cf014caf8a28de70ecb1b862a0efb6b8a2 Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Fri, 16 Jul 2021 12:19:06 -0400 Subject: Don't mention VS2013 in PR review instructions (#4384) Visual Studio 2013 is not supported anymore. --- CONTRIBUTING.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b46ae31e..1eb8b689 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -98,13 +98,6 @@ should pay particular attention to: scenarios? The respective SPIR-V dialects are slightly different. * Changes are made to a container while iterating through it. You have to be careful that iterators are not invalidated or that elements are not skipped. -* C++11 and VS2013. We generally assume that we have a C++11 compliant - compiler. However, on Windows, we still support Visual Studio 2013, which is - not fully C++11 compliant. See - [here](https://msdn.microsoft.com/en-us/library/hh567368.aspx). In - particular, note that it does not provide default move-constructors or - move-assignments for classes. In general, r-value references do not work the - way you might assume they do. * For SPIR-V transforms: The module is changed, but the analyses are not updated. For example, a new instruction is added, but the def-use manager is not updated. Later on, it is possible that the def-use manager will be used, -- cgit v1.2.3 From 8966cc2b2748351a04855dae6d04d3dc1606bc6e Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Fri, 16 Jul 2021 14:28:14 -0600 Subject: Add common enum for debug info instructions from either opencl or vulkan (#4377) Co-authored-by: baldurk --- BUILD.bazel | 4 +-- BUILD.gn | 1 + source/CMakeLists.txt | 1 + source/common_debug_info.h | 64 ++++++++++++++++++++++++++++++++++++++++++ source/opt/feature_manager.cpp | 7 +++++ source/opt/feature_manager.h | 8 ++++++ source/opt/instruction.cpp | 47 ++++++++++++++++++++++++++++++- source/opt/instruction.h | 20 +++++++++++++ 8 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 source/common_debug_info.h diff --git a/BUILD.bazel b/BUILD.bazel index 145ef744..4075441a 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -69,7 +69,7 @@ generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE) generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE) -generate_extinst_lang_headers("VulkanDebugInfo100", VKDEBUGINFO100_GRAMMAR_JSON_FILE) +generate_extinst_lang_headers("NonSemanticVulkanDebugInfo100", VKDEBUGINFO100_GRAMMAR_JSON_FILE) py_binary( name = "generate_registry_tables", @@ -107,7 +107,7 @@ cc_library( ":gen_enum_string_mapping", ":gen_extinst_lang_headers_DebugInfo", ":gen_extinst_lang_headers_OpenCLDebugInfo100", - ":gen_extinst_lang_headers_VulkanDebugInfo100", + ":gen_extinst_lang_headers_NonSemanticVulkanDebugInfo100", ":gen_glsl_tables_unified1", ":gen_opencl_tables_unified1", ":gen_registry_tables", diff --git a/BUILD.gn b/BUILD.gn index fb6d03fc..270814f7 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -416,6 +416,7 @@ static_library("spvtools") { "source/binary.cpp", "source/binary.h", "source/cfa.h", + "source/common_debug_info.h", "source/diagnostic.cpp", "source/diagnostic.h", "source/disassemble.cpp", diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 163505fe..e4568e35 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -234,6 +234,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h ${CMAKE_CURRENT_SOURCE_DIR}/binary.h ${CMAKE_CURRENT_SOURCE_DIR}/cfa.h + ${CMAKE_CURRENT_SOURCE_DIR}/common_debug_info.h ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h diff --git a/source/common_debug_info.h b/source/common_debug_info.h new file mode 100644 index 00000000..0ae85aa0 --- /dev/null +++ b/source/common_debug_info.h @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 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 SOURCE_COMMON_DEBUG_INFO_HEADER_H_ +#define SOURCE_COMMON_DEBUG_INFO_HEADER_H_ + +// This enum defines the known common set of instructions that are the same +// between OpenCL.DebugInfo.100 and NonSemantic.Vulkan.DebugInfo.100. +// note that NonSemantic.DebugInfo.100 instructions can still have slightly +// different encoding, as it does not use literals anywhere and only constants. +enum CommonDebugInfoInstructions { + CommonDebugInfoDebugInfoNone = 0, + CommonDebugInfoDebugCompilationUnit = 1, + CommonDebugInfoDebugTypeBasic = 2, + CommonDebugInfoDebugTypePointer = 3, + CommonDebugInfoDebugTypeQualifier = 4, + CommonDebugInfoDebugTypeArray = 5, + CommonDebugInfoDebugTypeVector = 6, + CommonDebugInfoDebugTypedef = 7, + CommonDebugInfoDebugTypeFunction = 8, + CommonDebugInfoDebugTypeEnum = 9, + CommonDebugInfoDebugTypeComposite = 10, + CommonDebugInfoDebugTypeMember = 11, + CommonDebugInfoDebugTypeInheritance = 12, + CommonDebugInfoDebugTypePtrToMember = 13, + CommonDebugInfoDebugTypeTemplate = 14, + CommonDebugInfoDebugTypeTemplateParameter = 15, + CommonDebugInfoDebugTypeTemplateTemplateParameter = 16, + CommonDebugInfoDebugTypeTemplateParameterPack = 17, + CommonDebugInfoDebugGlobalVariable = 18, + CommonDebugInfoDebugFunctionDeclaration = 19, + CommonDebugInfoDebugFunction = 20, + CommonDebugInfoDebugLexicalBlock = 21, + CommonDebugInfoDebugLexicalBlockDiscriminator = 22, + CommonDebugInfoDebugScope = 23, + CommonDebugInfoDebugNoScope = 24, + CommonDebugInfoDebugInlinedAt = 25, + CommonDebugInfoDebugLocalVariable = 26, + CommonDebugInfoDebugInlinedVariable = 27, + CommonDebugInfoDebugDeclare = 28, + CommonDebugInfoDebugValue = 29, + CommonDebugInfoDebugOperation = 30, + CommonDebugInfoDebugExpression = 31, + CommonDebugInfoDebugMacroDef = 32, + CommonDebugInfoDebugMacroUndef = 33, + CommonDebugInfoDebugImportedEntity = 34, + CommonDebugInfoDebugSource = 35, + CommonDebugInfoInstructionsMax = 0x7ffffff +}; + +#endif // SOURCE_COMMON_DEBUG_INFO_HEADER_H_ diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index ad70c1e4..db3abddf 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -80,6 +80,8 @@ void FeatureManager::AddExtInstImportIds(Module* module) { extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); extinst_importid_OpenCL100DebugInfo_ = module->GetExtInstImportId("OpenCL.DebugInfo.100"); + extinst_importid_Vulkan100DebugInfo_ = + module->GetExtInstImportId("NonSemantic.Vulkan.DebugInfo.100"); } bool operator==(const FeatureManager& a, const FeatureManager& b) { @@ -107,6 +109,11 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) { return false; } + if (a.extinst_importid_Vulkan100DebugInfo_ != + b.extinst_importid_Vulkan100DebugInfo_) { + return false; + } + return true; } } // namespace opt diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index 66d1cbac..4720c6dc 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h @@ -55,6 +55,10 @@ class FeatureManager { return extinst_importid_OpenCL100DebugInfo_; } + uint32_t GetExtInstImportId_Vulkan100DebugInfo() const { + return extinst_importid_Vulkan100DebugInfo_; + } + friend bool operator==(const FeatureManager& a, const FeatureManager& b); friend bool operator!=(const FeatureManager& a, const FeatureManager& b) { return !(a == b); @@ -92,6 +96,10 @@ class FeatureManager { // Common OpenCL100DebugInfo external instruction import ids, cached // for performance. uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; + + // Common NonSemanticVulkan100DebugInfo external instruction import ids, + // cached for performance. + uint32_t extinst_importid_Vulkan100DebugInfo_ = 0; }; } // namespace opt diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 1054a203..f93ad011 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -16,6 +16,7 @@ #include +#include "NonSemanticVulkanDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/disassemble.h" #include "source/opt/fold.h" @@ -32,7 +33,8 @@ const uint32_t kLoadBaseIndex = 0; const uint32_t kPointerTypeStorageClassIndex = 0; const uint32_t kTypeImageSampledIndex = 5; -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Vulkan.DebugInfo.100 +// extension instructions. const uint32_t kExtInstSetIdInIdx = 0; const uint32_t kExtInstInstructionInIdx = 1; const uint32_t kDebugScopeNumWords = 7; @@ -618,6 +620,49 @@ OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { GetSingleWordInOperand(kExtInstInstructionInIdx)); } +NonSemanticVulkanDebugInfo100Instructions Instruction::GetVulkan100DebugOpcode() + const { + if (opcode() != SpvOpExtInst) { + return NonSemanticVulkanDebugInfo100InstructionsMax; + } + + if (!context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) { + return NonSemanticVulkanDebugInfo100InstructionsMax; + } + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) != + context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) { + return NonSemanticVulkanDebugInfo100InstructionsMax; + } + + return NonSemanticVulkanDebugInfo100Instructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + +CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const { + if (opcode() != SpvOpExtInst) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t opencl_set_id = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + const uint32_t vulkan_set_id = + context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo(); + + if (!opencl_set_id && !vulkan_set_id) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx); + + if (used_set_id != opencl_set_id && used_set_id != vulkan_set_id) { + return CommonDebugInfoInstructionsMax; + } + + return CommonDebugInfoInstructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + bool Instruction::IsValidBaseImage() const { uint32_t tid = type_id(); if (tid == 0) { diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 3e557dd7..7cdfc44d 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -22,7 +22,9 @@ #include #include +#include "NonSemanticVulkanDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/common_debug_info.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_spirv_header.h" #include "source/opcode.h" @@ -550,10 +552,28 @@ class Instruction : public utils::IntrusiveNodeBase { // OpenCLDebugInfo100InstructionsMax. OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; + // Returns debug opcode of a NonSemantic.Vulkan.DebugInfo.100 instruction. If + // it is not a NonSemantic.Vulkan.DebugInfo.100 instruction, just returns + // NonSemanticVulkanDebugInfo100InstructionsMax. + NonSemanticVulkanDebugInfo100Instructions GetVulkan100DebugOpcode() const; + + // Returns debug opcode of an OpenCL.100.DebugInfo or + // NonSemantic.Vulkan.DebugInfo.100 instruction. Since these overlap, we + // return the OpenCLDebugInfo code + CommonDebugInfoInstructions GetCommonDebugOpcode() const; + // Returns true if it is an OpenCL.DebugInfo.100 instruction. bool IsOpenCL100DebugInstr() const { return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; } + // Returns true if it is a NonSemantic.Vulkan.DebugInfo.100 instruction. + bool IsVulkan100DebugInstr() const { + return GetVulkan100DebugOpcode() != + NonSemanticVulkanDebugInfo100InstructionsMax; + } + bool IsCommonDebugInstr() const { + return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax; + } // Returns true if this instructions a non-semantic instruction. bool IsNonSemanticInstruction() const; -- cgit v1.2.3 From 3a68a7274022f75d1a54baebda313820010b1c9c Mon Sep 17 00:00:00 2001 From: Chia-I Wu Date: Fri, 16 Jul 2021 13:30:04 -0700 Subject: CMake: add ENABLE_RTTI option (#4382) Fixes $ CXX=clang++ cmake -H. -Bout -DSPIRV_USE_SANITIZER=vptr $ make -C out ... clang: error: invalid argument '-fsanitize=vptr' not allowed with '-fno-rtti' --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 35689491..84a7bb7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ include(GNUInstallDirs) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_STANDARD 11) +option(ENABLE_RTTI "Enables RTTI" OFF) option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") @@ -178,11 +179,14 @@ function(spvtools_default_compile_options TARGET) target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS}) if (${COMPILER_IS_LIKE_GNU}) - target_compile_options(${TARGET} PRIVATE - -std=c++11 -fno-exceptions -fno-rtti) + target_compile_options(${TARGET} PRIVATE -std=c++11 -fno-exceptions) target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion -Wno-sign-conversion) + + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() # For good call stacks in profiles, keep the frame pointers. if(NOT "${SPIRV_PERF}" STREQUAL "") target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer) -- cgit v1.2.3 From f084bcfe2bf94d8d9867e7fd4baed78381539ea1 Mon Sep 17 00:00:00 2001 From: Mostafa Ashraf Date: Tue, 20 Jul 2021 11:03:58 +0200 Subject: spirv-fuzz: Support atomic operations opcode (#4348) This change captures the fact that the signedness of memory semantics and scope parameters of atomic operations does not matter. Fixes #4345. --- .../transformation_replace_id_with_synonym.cpp | 34 ++++ ...transformation_replace_id_with_synonym_test.cpp | 209 ++++++++++++++++++++- 2 files changed, 237 insertions(+), 6 deletions(-) diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp index 92ce751d..14555315 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -140,9 +140,43 @@ bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand( case SpvOpUGreaterThanEqual: case SpvOpSGreaterThanEqual: return true; + + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT, + // AtomicFloat64AddEXT. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index == 1 || use_in_operand_index == 2; + + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1 && use_in_operand_index <= 3; + + case SpvOpAtomicLoad: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicFlagTestAndSet: // Capability Kernel. + case SpvOpAtomicFlagClear: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1; + case SpvOpAccessChain: // The signedness of indices does not matter. return use_in_operand_index > 0; + default: // Conservatively assume that the id cannot be swapped in other // instructions. diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 5442f804..cebce8a0 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -2176,7 +2176,7 @@ TEST(TransformationReplaceIdWithSynonymTest, // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this // test so that it covers more atomic operations, and enable the test once the // issue is fixed. -TEST(TransformationReplaceIdWithSynonymTest, DISABLED_TypesAreCompatible) { +TEST(TransformationReplaceIdWithSynonymTest, TypesAreCompatible) { const std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -2188,8 +2188,39 @@ TEST(TransformationReplaceIdWithSynonymTest, DISABLED_TypesAreCompatible) { %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %9 = OpTypeInt 32 0 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %86 = OpTypeStruct %9 + %87 = OpTypePointer Workgroup %86 + %88 = OpVariable %87 Workgroup + %89 = OpTypePointer Workgroup %9 + %19 = OpConstant %9 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 7 + %20 = OpConstant %9 64 %4 = OpFunction %2 None %3 %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %90 = OpAccessChain %89 %88 %19 + %21 = OpAtomicLoad %6 %14 %15 %20 + %22 = OpAtomicExchange %6 %14 %15 %20 %16 + %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 + %24 = OpAtomicIIncrement %6 %14 %15 %20 + %25 = OpAtomicIDecrement %6 %14 %15 %20 + %26 = OpAtomicIAdd %6 %14 %15 %20 %16 + %27 = OpAtomicISub %6 %14 %15 %20 %16 + %28 = OpAtomicSMin %6 %14 %15 %20 %16 + %29 = OpAtomicUMin %9 %90 %15 %20 %18 + %30 = OpAtomicSMax %6 %14 %15 %20 %15 + %31 = OpAtomicUMax %9 %90 %15 %20 %18 + %32 = OpAtomicAnd %6 %14 %15 %20 %16 + %33 = OpAtomicOr %6 %14 %15 %20 %16 + %34 = OpAtomicXor %6 %14 %15 %20 %16 + OpAtomicStore %14 %15 %20 %16 OpReturn OpFunctionEnd )"; @@ -2208,7 +2239,7 @@ TEST(TransformationReplaceIdWithSynonymTest, DISABLED_TypesAreCompatible) { #ifndef NDEBUG ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( context.get(), SpvOpAtomicLoad, 0, int_type, uint_type), - "Invalid operand index"); + "Signedness check should not occur on a pointer operand."); #endif ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( context.get(), SpvOpAtomicLoad, 1, int_type, uint_type)); @@ -2219,17 +2250,183 @@ TEST(TransformationReplaceIdWithSynonymTest, DISABLED_TypesAreCompatible) { #ifndef NDEBUG ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( context.get(), SpvOpAtomicExchange, 0, int_type, uint_type), - "Invalid operand index"); + "Signedness check should not occur on a pointer operand."); #endif ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( context.get(), SpvOpAtomicExchange, 1, int_type, uint_type)); ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( context.get(), SpvOpAtomicExchange, 2, int_type, uint_type)); ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 2, int_type, uint_type)); + context.get(), SpvOpAtomicExchange, 3, int_type, uint_type)); + + // OpAtomicStore +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicStore, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicStore, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicStore, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicStore, 3, int_type, uint_type)); - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Similar for - // other atomic instructions + // OpAtomicCompareExchange +#ifndef NDEBUG + ASSERT_DEATH( + TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type)); + + // OpAtomicIIncrement +#ifndef NDEBUG + ASSERT_DEATH( + TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type)); + +// OpAtomicIDecrement +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicStore, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicStore, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicStore, 2, int_type, uint_type)); + +// OpAtomicIAdd +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicIAdd, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicIAdd, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicIAdd, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicIAdd, 3, int_type, uint_type)); + +// OpAtomicISub +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicISub, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicISub, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicISub, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicISub, 3, int_type, uint_type)); + +// OpAtomicSMin +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMin, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMin, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMin, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMin, 3, int_type, uint_type)); + +// OpAtomicUMin +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMin, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMin, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMin, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMin, 3, int_type, uint_type)); + +// OpAtomicSMax +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMax, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMax, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMax, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicSMax, 3, int_type, uint_type)); + +// OpAtomicUMax +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMax, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMax, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMax, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicUMax, 3, int_type, uint_type)); + +// OpAtomicAnd +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicAnd, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicAnd, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicAnd, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicAnd, 3, int_type, uint_type)); + +// OpAtomicOr +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicOr, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicOr, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicOr, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicOr, 3, int_type, uint_type)); + +// OpAtomicXor +#ifndef NDEBUG + ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicXor, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicXor, 1, int_type, uint_type)); + ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicXor, 2, int_type, uint_type)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( + context.get(), SpvOpAtomicXor, 3, int_type, uint_type)); } } // namespace -- cgit v1.2.3 From 033768c24bbc72d82fe23f14216e2f5f005a2fb9 Mon Sep 17 00:00:00 2001 From: Shiyu Liu Date: Tue, 20 Jul 2021 05:01:20 -0500 Subject: spirv-fuzz: TransformationWrapVectorSynonym that rewrites scalar operations using vectors (#4376) Adds a new transformation that rewrites a scalar operation (like OpFAdd, opISub) as an equivalent vector operation, adding a synonym between the scalar result and an appropriate component of the vector result. Fixes #4195. --- BUILD.gn | 4 + source/fuzz/CMakeLists.txt | 4 + source/fuzz/fuzzer.cpp | 2 + source/fuzz/fuzzer_context.cpp | 3 + source/fuzz/fuzzer_context.h | 11 + source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp | 122 ++ source/fuzz/fuzzer_pass_wrap_vector_synonym.h | 37 + .../fuzz/pass_management/repeated_pass_instances.h | 2 + .../repeated_pass_recommender_standard.cpp | 6 + source/fuzz/protobufs/spvtoolsfuzz.proto | 37 + source/fuzz/transformation.cpp | 4 + source/fuzz/transformation_wrap_vector_synonym.cpp | 182 +++ source/fuzz/transformation_wrap_vector_synonym.h | 77 ++ test/fuzz/CMakeLists.txt | 1 + .../transformation_wrap_vector_synonym_test.cpp | 1394 ++++++++++++++++++++ 15 files changed, 1886 insertions(+) create mode 100644 source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp create mode 100644 source/fuzz/fuzzer_pass_wrap_vector_synonym.h create mode 100644 source/fuzz/transformation_wrap_vector_synonym.cpp create mode 100644 source/fuzz/transformation_wrap_vector_synonym.h create mode 100644 test/fuzz/transformation_wrap_vector_synonym_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 270814f7..791d7a35 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1062,6 +1062,8 @@ if (build_with_chromium) { "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h", "source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp", "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h", + "source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp", + "source/fuzz/fuzzer_pass_wrap_vector_synonym.h", "source/fuzz/fuzzer_util.cpp", "source/fuzz/fuzzer_util.h", "source/fuzz/id_use_descriptor.cpp", @@ -1272,6 +1274,8 @@ if (build_with_chromium) { "source/fuzz/transformation_wrap_early_terminator_in_function.h", "source/fuzz/transformation_wrap_region_in_selection.cpp", "source/fuzz/transformation_wrap_region_in_selection.h", + "source/fuzz/transformation_wrap_vector_synonym.cpp", + "source/fuzz/transformation_wrap_vector_synonym.h", "source/fuzz/uniform_buffer_element_descriptor.cpp", "source/fuzz/uniform_buffer_element_descriptor.h", ] diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index a8061662..dd674dd0 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -124,6 +124,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_swap_functions.h fuzzer_pass_toggle_access_chain_instruction.h fuzzer_pass_wrap_regions_in_selections.h + fuzzer_pass_wrap_vector_synonym.h fuzzer_util.h id_use_descriptor.h instruction_descriptor.h @@ -230,6 +231,7 @@ if(SPIRV_BUILD_FUZZER) transformation_vector_shuffle.h transformation_wrap_early_terminator_in_function.h transformation_wrap_region_in_selection.h + transformation_wrap_vector_synonym.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -319,6 +321,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_swap_functions.cpp fuzzer_pass_toggle_access_chain_instruction.cpp fuzzer_pass_wrap_regions_in_selections.cpp + fuzzer_pass_wrap_vector_synonym.cpp fuzzer_util.cpp id_use_descriptor.cpp instruction_descriptor.cpp @@ -423,6 +426,7 @@ if(SPIRV_BUILD_FUZZER) transformation_vector_shuffle.cpp transformation_wrap_early_terminator_in_function.cpp transformation_wrap_region_in_selection.cpp + transformation_wrap_vector_synonym.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 fe88a55b..61c4ddbe 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -90,6 +90,7 @@ #include "source/fuzz/fuzzer_pass_swap_functions.h" #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" #include "source/fuzz/pass_management/repeated_pass_manager.h" #include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" @@ -214,6 +215,7 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, MaybeAddRepeatedPass( &pass_instances_); MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); // There is a theoretical possibility that no pass instances were created // until now; loop again if so. } while (pass_instances_.GetPasses().empty()); diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 9e9650f6..eff702af 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -162,6 +162,7 @@ const std::pair kChanceOfTogglingAccessChainInstruction = { 20, 90}; const std::pair kChanceOfWrappingRegionInSelection = {70, 90}; +const std::pair kChanceOfWrappingVectorSynonym = {10, 90}; // Default limits for various quantities that are chosen during fuzzing. // Keep them in alphabetical order. @@ -369,6 +370,8 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction); chance_of_wrapping_region_in_selection_ = ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection); + chance_of_wrapping_vector_synonym_ = + ChooseBetweenMinAndMax(kChanceOfWrappingVectorSynonym); } FuzzerContext::~FuzzerContext() = default; diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index ef2cc2c5..b3f8cc35 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -381,6 +381,10 @@ class FuzzerContext { return chance_of_wrapping_region_in_selection_; } + uint32_t GetChanceOfWrappingVectorSynonym() const { + return chance_of_wrapping_vector_synonym_; + } + // Other functions to control transformations. Keep them in alphabetical // order. uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const { @@ -425,6 +429,9 @@ class FuzzerContext { uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) { return random_generator_->RandomUint32(number_of_components); } + uint32_t GetRandomIndexForWrappingVector(uint32_t vector_width) { + return random_generator_->RandomUint32(vector_width); + } int64_t GetRandomValueForStepConstantInLoop() { return random_generator_->RandomUint64(UINT64_MAX); } @@ -462,6 +469,9 @@ class FuzzerContext { // Ensure that the number of unused components is non-zero. return random_generator_->RandomUint32(max_unused_component_count) + 1; } + uint32_t GetWidthOfWrappingVector() { + return 2 + random_generator_->RandomUint32(3); + } bool GoDeeperInConstantObfuscation(uint32_t depth) { return go_deeper_in_constant_obfuscation_(depth, random_generator_.get()); } @@ -562,6 +572,7 @@ class FuzzerContext { uint32_t chance_of_swapping_functions_; uint32_t chance_of_toggling_access_chain_instruction_; uint32_t chance_of_wrapping_region_in_selection_; + uint32_t chance_of_wrapping_vector_synonym_; // Limits associated with various quantities for which random values are // chosen during fuzzing. diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp new file mode 100644 index 00000000..3f324df8 --- /dev/null +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_wrap_vector_synonym.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_composite_construct.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +void FuzzerPassWrapVectorSynonym::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + + // Randomly decide whether to wrap it to a vector operation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) { + return; + } + + // The transformation is not applicable if the instruction has missing + // result id, type id, or is not supported type. + if (!TransformationWrapVectorSynonym::IsInstructionSupported( + GetIRContext(), *instruction_iterator)) { + return; + } + + // The transformation will not be applicable if the id of the scalar + // operation is irrelevant. + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + instruction_iterator->result_id())) { + return; + } + + // It must be valid to insert an OpCompositeConstruct instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeConstruct, instruction_iterator)) { + return; + } + // Get the scalar type represented by the targeted instruction id. + uint32_t operand_type_id = instruction_iterator->type_id(); + + // Get a random vector size from 2 to 4. + uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector(); + + // Randomly choose a position that target ids should be placed at. + // The position is in range [0, n - 1], where n is the size of the + // vector. + uint32_t position = + GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size); + + // Target ids are the two scalar ids from the original instruction. + uint32_t target_id1 = instruction_iterator->GetSingleWordInOperand(0); + uint32_t target_id2 = instruction_iterator->GetSingleWordInOperand(1); + + // Stores the ids of scalar constants. + std::vector vec1_components; + std::vector vec2_components; + + // Populate components based on vector type and size. + for (uint32_t i = 0; i < vector_size; ++i) { + if (i == position) { + vec1_components.emplace_back(target_id1); + vec2_components.emplace_back(target_id2); + } else { + vec1_components.emplace_back( + FindOrCreateZeroConstant(operand_type_id, true)); + vec2_components.emplace_back( + FindOrCreateZeroConstant(operand_type_id, true)); + } + } + + // Add two OpCompositeConstruct to the module with result id returned. + + // Add the first OpCompositeConstruct that wraps the id of the first + // operand. + uint32_t result_id1 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + operand_type_id, vec1_components, instruction_descriptor, + result_id1)); + + // Add the second OpCompositeConstruct that wraps the id of the second + // operand. + uint32_t result_id2 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + operand_type_id, vec2_components, instruction_descriptor, + result_id2)); + + // Apply transformation to do vector operation and add synonym between + // the result vector id and the id of the original instruction. + ApplyTransformation(TransformationWrapVectorSynonym( + instruction_iterator->result_id(), result_id1, result_id2, + GetFuzzerContext()->GetFreshId(), position)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.h b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h new file mode 100644 index 00000000..647d99d2 --- /dev/null +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h @@ -0,0 +1,37 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly wrap a scalar operation into a vector operation. +class FuzzerPassWrapVectorSynonym : public FuzzerPass { + public: + FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ diff --git a/source/fuzz/pass_management/repeated_pass_instances.h b/source/fuzz/pass_management/repeated_pass_instances.h index 80ac0875..da61fdab 100644 --- a/source/fuzz/pass_management/repeated_pass_instances.h +++ b/source/fuzz/pass_management/repeated_pass_instances.h @@ -73,6 +73,7 @@ #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" namespace spvtools { namespace fuzz { @@ -168,6 +169,7 @@ class RepeatedPassInstances { REPEATED_PASS_INSTANCE(SplitBlocks); REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands); REPEATED_PASS_INSTANCE(WrapRegionsInSelections); + REPEATED_PASS_INSTANCE(WrapVectorSynonym); #undef REPEATED_PASS_INSTANCE public: diff --git a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp index a933848d..6c61c0d4 100644 --- a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp +++ b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp @@ -351,6 +351,12 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations( pass_instances_->GetReplaceIrrelevantIds(), pass_instances_->GetFlattenConditionalBranches()}); } + if (&pass == pass_instances_->GetWrapVectorSynonym()) { + // This transformation introduces synonym facts and irrelevant ids. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + assert(false && "Unreachable: every fuzzer pass should be dealt with."); return {}; } diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 657e8076..5618c140 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -559,6 +559,7 @@ message Transformation { TransformationExpandVectorReduction expand_vector_reduction = 85; TransformationSwapFunctionVariables swap_function_variables = 86; TransformationSwapTwoFunctions swap_two_functions = 87; + TransformationWrapVectorSynonym wrap_vector_synonym = 88; // Add additional option using the next available number. } } @@ -2371,3 +2372,39 @@ message TransformationWrapRegionInSelection { bool branch_condition = 3; } + +message TransformationWrapVectorSynonym { + // A transformation that wraps an arithmetic operation into a vector operation + // and get the result of the original operation from the corresponding index. + // For instance, for this transformation, an scalar operation between two scalars: + // define op ∈ {+, -, *} + // c = a op b + // + // requires the availability of two vectors: + // + // va = vector(..., a, ...) + // vb = vector(..., b, ...) + // + // where a and b are in the same position i in each of their correponding vector + // and a is synonymous with va[i] and b is synonymous with vb[i]. + // + // The transformation then add an instruction vc = va op vb where c is synonymous + // with vc[i]. + + // The result if of the original scalar operation instruction. + uint32 instruction_id = 1; + + // The result id for the first vector that contains the first value of the scalar operation. + uint32 vector_operand1 = 2; + + // The result id for the second vector that contains the second value of the scalar operation. + uint32 vector_operand2 = 3; + + // A fresh id for the resulted vector from the addition of the first and second vector. + uint32 fresh_id = 4; + + // The position in the vector where the value of original instruction is located. Must be in + // the corresponding vector range. + uint32 scalar_position = 5; + +} diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index 4ea0c773..70a302b7 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -104,6 +104,7 @@ #include "source/fuzz/transformation_vector_shuffle.h" #include "source/fuzz/transformation_wrap_early_terminator_in_function.h" #include "source/fuzz/transformation_wrap_region_in_selection.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" #include "source/util/make_unique.h" namespace spvtools { @@ -382,6 +383,9 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kWrapRegionInSelection: return MakeUnique( message.wrap_region_in_selection()); + case protobufs::Transformation::TransformationCase::kWrapVectorSynonym: + return MakeUnique( + message.wrap_vector_synonym()); case protobufs::Transformation::TRANSFORMATION_NOT_SET: assert(false && "An unset transformation was encountered."); return nullptr; diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp new file mode 100644 index 00000000..86eb51c8 --- /dev/null +++ b/source/fuzz/transformation_wrap_vector_synonym.cpp @@ -0,0 +1,182 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_wrap_vector_synonym.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message) + : message_(std::move(message)) {} + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2, + uint32_t fresh_id, uint32_t pos) { + message_.set_instruction_id(instruction_id); + message_.set_vector_operand1(vector_operand1); + message_.set_vector_operand2(vector_operand2); + message_.set_fresh_id(fresh_id); + message_.set_scalar_position(pos); +} + +bool TransformationWrapVectorSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + const opt::Instruction* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + + // |instruction_id| must refer to an existing instruction. + if (instruction == nullptr) { + return false; + } + + if (!IsInstructionSupported(ir_context, *instruction)) { + return false; + } + + assert(!transformation_context.GetFactManager()->IdIsIrrelevant( + instruction->result_id()) && + "Result id of the scalar operation must be relevant."); + + // |vector_operand1| and |vector_operand2| must exist. + auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1()); + auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2()); + + if (vec1 == nullptr || vec2 == nullptr) { + return false; + } + + // The 2 vectors must be the same valid vector type. + auto vec1_type_id = vec1->type_id(); + auto vec2_type_id = vec2->type_id(); + + if (vec1_type_id != vec2_type_id) { + return false; + } + + if (ir_context->get_def_use_mgr()->GetDef(vec1_type_id)->opcode() != + SpvOpTypeVector) { + return false; + } + + // |scalar_position| needs to be a non-negative integer less than the vector + // length. + // OpTypeVector instruction has the component count at index 2. + if (message_.scalar_position() >= ir_context->get_def_use_mgr() + ->GetDef(vec1_type_id) + ->GetSingleWordInOperand(1)) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand1(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand2(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) { + return false; + } + + return true; +} + +void TransformationWrapVectorSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Create an instruction descriptor for the original instruction. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + auto destination_block = ir_context->get_instr_block(instruction); + + // Populate input operand list with two vectors for vector operation. + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}}); + + // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1 + // %result_id2. + auto vec_type_id = ir_context->get_def_use_mgr() + ->GetDef(message_.vector_operand1()) + ->type_id(); + auto new_instruction = MakeUnique( + ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(), + std::move(in_operands)); + auto new_instruction_ptr = new_instruction.get(); + instruction->InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, destination_block); + + // Add |fresh_id| to id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Add synonyms between |fresh_id| and |instruction_id|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}), + MakeDataDescriptor(message_.instruction_id(), {})); +} + +protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_wrap_vector_synonym() = message_; + return result; +} + +std::unordered_set TransformationWrapVectorSynonym::GetFreshIds() + const { + return std::unordered_set{message_.fresh_id()}; +} + +bool TransformationWrapVectorSynonym::IsInstructionSupported( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + if (!instruction.result_id() || !instruction.type_id()) { + return false; + } + auto type_instruction = + ir_context->get_def_use_mgr()->GetDef(instruction.type_id()); + + if ((type_instruction->opcode() != SpvOpTypeInt && + type_instruction->opcode() != SpvOpTypeFloat)) { + return false; + } + + switch (instruction.opcode()) { + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + case SpvOpFAdd: + case SpvOpFSub: + case SpvOpFMul: + return true; + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_wrap_vector_synonym.h b/source/fuzz/transformation_wrap_vector_synonym.h new file mode 100644 index 00000000..008211a2 --- /dev/null +++ b/source/fuzz/transformation_wrap_vector_synonym.h @@ -0,0 +1,77 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_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 TransformationWrapVectorSynonym : public Transformation { + public: + explicit TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message); + + TransformationWrapVectorSynonym(uint32_t instruction_id, + uint32_t vector_operand1, + uint32_t vector_operand2, uint32_t fresh_id, + uint32_t pos); + // - |instruction_id| must be the id of a supported arithmetic operation + // and must be relevant. + // - |vector_operand1| and |vector_operand2| represents the result ids of the + // two vector operands. + // - |fresh_id| is an unused id that will be used as a result id of the + // created instruction. + // - |vector_operand1| and |vector_operand2| must have the same vector type + // that is supported by this transformation. + // - |pos| is an index of the operands of |instruction_id| in the + // |vector_operand1| and |vector_operand2|. It must be less than the size + // of those vector operands. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a new instruction before the |instruction_id| with |fresh_id| + // result id and |instruction_id|'s opcode. The added instruction has + // two operands: |vector_operand1| and |vector_operand2| and its type + // id is equal to the type ids of those operands. A new fact is added + // to the fact manager specifying that |fresh_id[pos]| is synonymous + // to |instruction_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + protobufs::Transformation ToMessage() const override; + + // Checks whether the instruction given is supported by the transformation. + // A valid instruction must: + // - has both result id and type id. + // - is a supported scalar operation instruction. + // - has a supported type that is either int or float. + static bool IsInstructionSupported(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + private: + protobufs::TransformationWrapVectorSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 8acebde6..56af0b9d 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -122,6 +122,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_vector_shuffle_test.cpp transformation_wrap_early_terminator_in_function_test.cpp transformation_wrap_region_in_selection_test.cpp + transformation_wrap_vector_synonym_test.cpp uniform_buffer_element_descriptor_test.cpp) if (${SPIRV_ENABLE_LONG_FUZZER_TESTS}) diff --git a/test/fuzz/transformation_wrap_vector_synonym_test.cpp b/test/fuzz/transformation_wrap_vector_synonym_test.cpp new file mode 100644 index 00000000..dae78a5d --- /dev/null +++ b/test/fuzz/transformation_wrap_vector_synonym_test.cpp @@ -0,0 +1,1394 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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_wrap_vector_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationWrapVectorSynonym, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Vec Type Id | Vector Type | Element Type id | Element Type | + // ------------+----------------+------------------+-----------------+ + // 12 | vec2 | 6 | int32 | + // 24 | vec3 | 18 | uint32 | + // 37 | vec4 | 31 | float | + + // Instruction Id | Opcode | Type Id | constant id 1 | constant id 2 | + // ---------------+---------+---------+---------------+---------------+ + // 50 | OpIAdd | 6 | 48 | 49 | + // 54 | OpISub | 6 | 52 | 53 | + // 58 | OpIMul | 6 | 56 | 57 | + // 62 | OpSDiv | 6 | 60 | 61 | + // 66 | OpIAdd | 18 | 64 | 65 | + // 70 | OpISub | 18 | 68 | 69 | + // 74 | OpIMul | 18 | 72 | 73 | + // 78 | OpUDiv | 18 | 76 | 77 | + // 82 | OpFAdd | 31 | 80 | 81 | + // 86 | OpFSub | 31 | 84 | 85 | + // 90 | OpFMul | 31 | 88 | 89 | + // 94 | OpFDiv | 31 | 92 | 93 | + + // Assert that the target scalar instruction result id is relevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {})); + + // The following are all invalid use. + { + // Bad: Instruction id does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad1(103, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad1.IsApplicable(context.get(), transformation_context)); + + // Bad: Instruction id given is not of a valid arithmetic operation typed + // instruction. + TransformationWrapVectorSynonym wrap_add_int_bad2(80, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad1.IsApplicable(context.get(), transformation_context)); + + // Bad: the id for the first vector does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad3(50, 105, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad3.IsApplicable(context.get(), transformation_context)); + + // Bad: the id for the second vector does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad4(50, 100, 105, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad4.IsApplicable(context.get(), transformation_context)); + + // Bad: vector id is not fresh. + TransformationWrapVectorSynonym wrap_add_int_bad6(50, 100, 101, 94, 1); + ASSERT_FALSE( + wrap_add_int_bad6.IsApplicable(context.get(), transformation_context)); + + // Bad: The position goes out of bound for the given vector type. + TransformationWrapVectorSynonym wrap_add_int_bad8(50, 100, 101, 102, 2); + ASSERT_FALSE( + wrap_add_int_bad8.IsApplicable(context.get(), transformation_context)); + + // Bad: The original instruction is not a valid scalar operation + // instruction. + TransformationWrapVectorSynonym wrap_add_int(27, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int.IsApplicable(context.get(), transformation_context)); + } + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_int(50, 100, 101, 102, 1); + ASSERT_TRUE(wrap_add_int.IsApplicable(context.get(), transformation_context)); + // Insert an arithmetic instruction of the same type to add two vectors. + ApplyAndCheckFreshIds(wrap_add_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should be + // synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {}))); + + // After applying transformations, the instruction: + // + // %102 = OpIAdd %12 %100 %101 + // + // should be added before: + // + // %50 = OpIAdd %6 %48 %49 + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %102 = OpIAdd %12 %100 %101 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, OperationSupportTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %52 %52 + %101 = OpCompositeConstruct %12 %53 %53 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %103 = OpCompositeConstruct %12 %56 %56 + %104 = OpCompositeConstruct %12 %57 %57 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %106 = OpCompositeConstruct %24 %64 %64 %64 + %107 = OpCompositeConstruct %24 %65 %65 %65 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %109 = OpCompositeConstruct %24 %68 %68 %68 + %110 = OpCompositeConstruct %24 %69 %69 %69 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %112 = OpCompositeConstruct %24 %72 %72 %72 + %113 = OpCompositeConstruct %24 %73 %73 %73 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %115 = OpCompositeConstruct %37 %80 %80 %80 %80 + %116 = OpCompositeConstruct %37 %81 %81 %81 %81 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %118 = OpCompositeConstruct %37 %84 %84 %84 %84 + %119 = OpCompositeConstruct %37 %85 %85 %85 %85 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %121 = OpCompositeConstruct %37 %88 %88 %88 %88 + %122 = OpCompositeConstruct %37 %89 %89 %89 %89 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + { + // Add synonym facts between the vector operands at pos and the operands to + // the scalar instruction. + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(52, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(53, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {0}), MakeDataDescriptor(56, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {0}), MakeDataDescriptor(57, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {2}), MakeDataDescriptor(64, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(107, {2}), MakeDataDescriptor(65, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(109, {2}), MakeDataDescriptor(68, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(110, {2}), MakeDataDescriptor(69, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(112, {1}), MakeDataDescriptor(72, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(113, {1}), MakeDataDescriptor(73, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(115, {2}), MakeDataDescriptor(80, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(116, {2}), MakeDataDescriptor(81, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(118, {3}), MakeDataDescriptor(84, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(119, {3}), MakeDataDescriptor(85, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(121, {1}), MakeDataDescriptor(88, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(122, {1}), MakeDataDescriptor(89, {})); + } + + // Test OpISub for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_int(54, 100, 101, 102, 1); + ASSERT_TRUE( + wrap_sub_int.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_sub_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(54, {}))); + } + + // Test OpIMul for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_int(58, 103, 104, 105, 0); + ASSERT_TRUE( + wrap_mul_int.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(105, {0}), MakeDataDescriptor(58, {}))); + } + + // Test OpIAdd for unsigned integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_uint(66, 106, 107, 108, 2); + ASSERT_TRUE( + wrap_add_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {2}), MakeDataDescriptor(66, {}))); + } + + // Test OpISub for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_uint(70, 109, 110, 111, 2); + ASSERT_TRUE( + wrap_sub_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_sub_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(111, {2}), MakeDataDescriptor(70, {}))); + } + + // Test OpIMul for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_uint(74, 112, 113, 114, 1); + ASSERT_TRUE( + wrap_mul_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(114, {1}), MakeDataDescriptor(74, {}))); + } + + // Test OpFAdd for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_float(82, 115, 116, 117, 2); + ASSERT_TRUE( + wrap_add_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {2}), MakeDataDescriptor(82, {}))); + } + + // Test OpFSub for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_float(86, 118, 119, 120, 3); + ASSERT_TRUE( + wrap_add_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(120, {3}), MakeDataDescriptor(86, {}))); + } + + // Test OpFMul for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_float(90, 121, 122, 123, 1); + ASSERT_TRUE( + wrap_mul_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(123, {1}), MakeDataDescriptor(90, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %52 %52 + %101 = OpCompositeConstruct %12 %53 %53 + %102 = OpISub %12 %100 %101 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %103 = OpCompositeConstruct %12 %56 %56 + %104 = OpCompositeConstruct %12 %57 %57 + %105 = OpIMul %12 %103 %104 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %106 = OpCompositeConstruct %24 %64 %64 %64 + %107 = OpCompositeConstruct %24 %65 %65 %65 + %108 = OpIAdd %24 %106 %107 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %109 = OpCompositeConstruct %24 %68 %68 %68 + %110 = OpCompositeConstruct %24 %69 %69 %69 + %111 = OpISub %24 %109 %110 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %112 = OpCompositeConstruct %24 %72 %72 %72 + %113 = OpCompositeConstruct %24 %73 %73 %73 + %114 = OpIMul %24 %112 %113 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %115 = OpCompositeConstruct %37 %80 %80 %80 %80 + %116 = OpCompositeConstruct %37 %81 %81 %81 %81 + %117 = OpFAdd %37 %115 %116 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %118 = OpCompositeConstruct %37 %84 %84 %84 %84 + %119 = OpCompositeConstruct %37 %85 %85 %85 %85 + %120 = OpFSub %37 %118 %119 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %121 = OpCompositeConstruct %37 %88 %88 %88 %88 + %122 = OpCompositeConstruct %37 %89 %89 %89 %89 + %123 = OpFMul %37 %121 %122 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, DivSupportTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %60 %60 + %101 = OpCompositeConstruct %12 %61 %61 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %102 = OpCompositeConstruct %24 %76 %76 %76 + %103 = OpCompositeConstruct %24 %77 %77 %77 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %104 = OpCompositeConstruct %37 %92 %92 %92 %92 + %105 = OpCompositeConstruct %37 %93 %93 %93 %93 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(60, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(61, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(76, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {1}), MakeDataDescriptor(77, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {1}), MakeDataDescriptor(92, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(105, {1}), MakeDataDescriptor(93, {})); + + // Div operations are not currently supported. + { + TransformationWrapVectorSynonym wrap_div_bad1(62, 100, 101, 106, 1); + ASSERT_FALSE( + wrap_div_bad1.IsApplicable(context.get(), transformation_context)); + + TransformationWrapVectorSynonym wrap_div_bad2(78, 102, 103, 106, 1); + ASSERT_FALSE( + wrap_div_bad2.IsApplicable(context.get(), transformation_context)); + + TransformationWrapVectorSynonym wrap_div_bad3(94, 104, 105, 106, 1); + ASSERT_FALSE( + wrap_div_bad3.IsApplicable(context.get(), transformation_context)); + } +} + +TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) { + std::string shader = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 64 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 64 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 64 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %103 = OpCompositeConstruct %24 %68 %68 %68 + %104 = OpCompositeConstruct %24 %69 %69 %69 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %106 = OpCompositeConstruct %37 %88 %88 %88 %88 + %107 = OpCompositeConstruct %37 %89 %89 %89 %89 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Vec Type Id | Vector Type | Element Type id | Element Type | + // ------------+----------------+------------------+-----------------+ + // 12 | vec2 | 6 | int64 | + // 24 | vec3 | 18 | uint64 | + // 37 | vec4 | 31 | float64 | + + // Test support for 64-bit signed int. + { + // Assert that the target scalar instruction result id is relevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_int64(50, 100, 101, 102, 1); + ASSERT_TRUE( + wrap_add_int64.IsApplicable(context.get(), transformation_context)); + // Insert an arithmetic instruction of the same type to add two vectors. + ApplyAndCheckFreshIds(wrap_add_int64, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {}))); + } + + // Test support for 64-bit unsigned int. + { + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(70)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {2}), MakeDataDescriptor(68, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {2}), MakeDataDescriptor(69, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_uint64(70, 103, 104, 105, 2); + ASSERT_TRUE( + wrap_sub_uint64.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(wrap_sub_uint64, context.get(), + &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(105, {2}), MakeDataDescriptor(70, {}))); + } + + // Test support for 64-bit float. + { + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(90)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {3}), MakeDataDescriptor(88, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(107, {3}), MakeDataDescriptor(89, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_float64(90, 106, 107, 108, 3); + ASSERT_TRUE( + wrap_mul_float64.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(wrap_mul_float64, context.get(), + &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {3}), MakeDataDescriptor(90, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 64 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 64 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 64 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %102 = OpIAdd %12 %100 %101 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %103 = OpCompositeConstruct %24 %68 %68 %68 + %104 = OpCompositeConstruct %24 %69 %69 %69 + %105 = OpISub %24 %103 %104 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %106 = OpCompositeConstruct %37 %88 %88 %88 %88 + %107 = OpCompositeConstruct %37 %89 %89 %89 %89 + %108 = OpFMul %37 %106 %107 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools -- cgit v1.2.3 From d9f89257855a2784323512cd9568b6610bcae581 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Wed, 21 Jul 2021 10:04:38 -0600 Subject: spirv-opt: Where possible make code agnostic of opencl/vulkan debuginfo (#4385) Co-authored-by: baldurk --- source/opt/aggressive_dead_code_elim_pass.cpp | 12 +++++------- source/opt/copy_prop_arrays.cpp | 25 ++++++++++++------------- source/opt/dead_insert_elim_pass.cpp | 2 +- source/opt/inline_pass.cpp | 10 ++++------ source/opt/instruction.cpp | 4 ++-- source/opt/ir_context.cpp | 6 +++--- source/opt/ir_context.h | 4 ++-- source/opt/ir_loader.cpp | 12 +++++++----- source/opt/local_access_chain_convert_pass.cpp | 4 ++-- source/opt/local_single_block_elim_pass.cpp | 6 +++--- source/opt/local_single_store_elim_pass.cpp | 12 ++++++------ source/opt/mem_pass.cpp | 7 +++---- source/opt/module.h | 8 ++++---- source/opt/private_to_local_pass.cpp | 6 ++---- source/opt/reduce_load_size.cpp | 2 +- source/opt/scalar_replacement_pass.cpp | 10 +++++----- source/opt/value_number_table.cpp | 2 +- source/opt/vector_dce.cpp | 4 ++-- 18 files changed, 65 insertions(+), 71 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 8c248b39..90d30e9a 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -474,14 +474,13 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { ProcessLoad(func, varId); } // If DebugDeclare, process as load of variable - } else if (liveInst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + } else if (liveInst->GetCommonDebugOpcode() == + CommonDebugInfoDebugDeclare) { uint32_t varId = liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); ProcessLoad(func, varId); // If DebugValue with Deref, process as load of variable - } else if (liveInst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugValue) { + } else if (liveInst->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { uint32_t varId = context() ->get_debug_info_mgr() ->GetVariableIdOfDebugValueUsedForDeclare(liveInst); @@ -652,7 +651,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { // For each DebugInfo GlobalVariable keep all operands except the Variable. // Later, if the variable is dead, we will set the operand to DebugInfoNone. for (auto& dbg : get_module()->ext_inst_debuginfo()) { - if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable) + if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; dbg.ForEachInId([this](const uint32_t* iid) { Instruction* inInst = get_def_use_mgr()->GetDef(*iid); @@ -877,8 +876,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { if (!IsDead(&dbg)) continue; // Save GlobalVariable if its variable is live, otherwise null out variable // index - if (dbg.GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugGlobalVariable) { + if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex); Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); if (!IsDead(var_inst)) continue; diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp index 67a97b6a..f505d8a5 100644 --- a/source/opt/copy_prop_arrays.cpp +++ b/source/opt/copy_prop_arrays.cpp @@ -29,10 +29,10 @@ const uint32_t kCompositeExtractObjectInOperand = 0; const uint32_t kTypePointerStorageClassInIdx = 0; const uint32_t kTypePointerPointeeInIdx = 1; -bool IsOpenCL100DebugDeclareOrValue(Instruction* di) { - auto dbg_opcode = di->GetOpenCL100DebugOpcode(); - return dbg_opcode == OpenCLDebugInfo100DebugDeclare || - dbg_opcode == OpenCLDebugInfo100DebugValue; +bool IsDebugDeclareOrValue(Instruction* di) { + auto dbg_opcode = di->GetCommonDebugOpcode(); + return dbg_opcode == CommonDebugInfoDebugDeclare || + dbg_opcode == CommonDebugInfoDebugValue; } } // namespace @@ -194,7 +194,7 @@ bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst, return ptr_inst->opcode() == SpvOpVariable && store_inst->GetSingleWordInOperand(kStorePointerInOperand) == ptr_inst->result_id(); - } else if (IsOpenCL100DebugDeclareOrValue(use)) { + } else if (IsDebugDeclareOrValue(use)) { return true; } // Some other instruction. Be conservative. @@ -500,7 +500,7 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, const_mgr, type](Instruction* use, uint32_t) { - if (IsOpenCL100DebugDeclareOrValue(use)) return true; + if (IsDebugDeclareOrValue(use)) return true; switch (use->opcode()) { case SpvOpLoad: { @@ -592,9 +592,9 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, Instruction* use = pair.first; uint32_t index = pair.second; - if (use->IsOpenCL100DebugInstr()) { - switch (use->GetOpenCL100DebugOpcode()) { - case OpenCLDebugInfo100DebugDeclare: { + if (use->IsCommonDebugInstr()) { + switch (use->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: { if (new_ptr_inst->opcode() == SpvOpVariable || new_ptr_inst->opcode() == SpvOpFunctionParameter) { context()->ForgetUses(use); @@ -608,9 +608,8 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, context()->ForgetUses(use); // Change DebugDeclare to DebugValue. - use->SetOperand( - index - 2, - {static_cast(OpenCLDebugInfo100DebugValue)}); + use->SetOperand(index - 2, + {static_cast(CommonDebugInfoDebugValue)}); use->SetOperand(index, {new_ptr_inst->result_id()}); // Add Deref operation. @@ -625,7 +624,7 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, } break; } - case OpenCLDebugInfo100DebugValue: + case CommonDebugInfoDebugValue: context()->ForgetUses(use); use->SetOperand(index, {new_ptr_inst->result_id()}); context()->AnalyzeUses(use); diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp index 46f4f124..fb5c1634 100644 --- a/source/opt/dead_insert_elim_pass.cpp +++ b/source/opt/dead_insert_elim_pass.cpp @@ -196,7 +196,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) { } const uint32_t id = ii->result_id(); get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) { - if (user->IsOpenCL100DebugInstr()) return; + if (user->IsCommonDebugInstr()) return; switch (user->opcode()) { case SpvOpCompositeInsert: case SpvOpPhi: diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index 8159ebf7..ce3bf7f3 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp @@ -158,8 +158,8 @@ bool InlinePass::CloneAndMapLocals( auto callee_block_itr = calleeFn->begin(); auto callee_var_itr = callee_block_itr->begin(); while (callee_var_itr->opcode() == SpvOp::SpvOpVariable || - callee_var_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + callee_var_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugDeclare) { if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) { ++callee_var_itr; continue; @@ -300,8 +300,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers( UptrVectorIterator callee_first_block_itr) { auto callee_itr = callee_first_block_itr->begin(); while (callee_itr->opcode() == SpvOp::SpvOpVariable || - callee_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { if (callee_itr->opcode() == SpvOp::SpvOpVariable && callee_itr->NumInOperands() == 2) { assert(callee2caller.count(callee_itr->result_id()) && @@ -315,8 +314,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers( context()->get_debug_info_mgr()->BuildDebugScope( callee_itr->GetDebugScope(), inlined_at_ctx)); } - if (callee_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + if (callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { InlineSingleInstruction( callee2caller, new_blk_ptr->get(), &*callee_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index f93ad011..e07fd624 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -985,10 +985,10 @@ 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; + CommonDebugInfoInstructions dbg_opcode = CommonDebugInfoDebugScope; if (GetLexicalScope() == kNoDebugScope) { num_words = kDebugNoScopeNumWords; - dbg_opcode = OpenCLDebugInfo100DebugNoScope; + dbg_opcode = CommonDebugInfoDebugNoScope; } else if (GetInlinedAt() == kNoInlinedAt) { num_words = kDebugScopeNumWordsWithoutInlinedAt; } diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 74f38786..90873461 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -30,7 +30,8 @@ static const int kSpvDecorateBuiltinInIdx = 2; static const int kEntryPointInterfaceInIdx = 3; static const int kEntryPointFunctionIdInIdx = 1; -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Vulkan.DebugInfo.100 +// extension instructions. static const uint32_t kDebugFunctionOperandFunctionIndex = 13; static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; @@ -437,8 +438,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) { if (opcode == SpvOpVariable || IsConstantInst(opcode)) { for (auto it = module()->ext_inst_debuginfo_begin(); it != module()->ext_inst_debuginfo_end(); ++it) { - if (it->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugGlobalVariable) + if (it->GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex); if (operand.words[0] == id) { diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index f5f38a64..3b89d85c 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -190,8 +190,8 @@ class IRContext { inline IteratorRange debugs3() const; // Iterators for debug info instructions (excluding OpLine & OpNoLine) - // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 - // or DebugInfo extension placed between section 9 and 10. + // contained in this module. These are OpExtInst for DebugInfo extension + // placed between section 9 and 10. inline Module::inst_iterator ext_inst_debuginfo_begin(); inline Module::inst_iterator ext_inst_debuginfo_end(); inline IteratorRange ext_inst_debuginfo(); diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index e443ebb5..77f90f19 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -53,10 +53,12 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { // 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) { + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + if (ext_inst_key == CommonDebugInfoDebugScope) { uint32_t inlined_at = 0; if (inst->num_words > kInlinedAtIndex) inlined_at = inst->words[kInlinedAtIndex]; @@ -65,7 +67,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { module()->SetContainsDebugInfo(); return true; } - if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) { + if (ext_inst_key == CommonDebugInfoDebugNoScope) { last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); module()->SetContainsDebugInfo(); return true; diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index 5485c038..e8dbdfc4 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -184,8 +184,8 @@ bool LocalAccessChainConvertPass::IsConstantIndexAccessChain( bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) { if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue || - user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { return true; } SpvOp op = user->opcode(); diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index a964c4c6..a742e3b1 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -31,9 +31,9 @@ const uint32_t kStoreValIdInIdx = 1; bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) { if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { - auto dbg_op = user->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { return true; } SpvOp op = user->opcode(); diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index f3864a8d..9701d10e 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -224,9 +224,9 @@ Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( case SpvOpCopyObject: break; case SpvOpExtInst: { - auto dbg_op = user->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { break; } return nullptr; @@ -293,9 +293,9 @@ bool LocalSingleStoreElimPass::RewriteLoads( bool modified = false; for (Instruction* use : uses) { if (use->opcode() == SpvOpStore) continue; - auto dbg_op = use->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) + auto dbg_op = use->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) continue; if (use->opcode() == SpvOpLoad && dominator_analysis->Dominates(store_inst, use)) { diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp index 57387984..72442a99 100644 --- a/source/opt/mem_pass.cpp +++ b/source/opt/mem_pass.cpp @@ -20,7 +20,6 @@ #include #include -#include "OpenCLDebugInfo100.h" #include "source/cfa.h" #include "source/opt/basic_block.h" #include "source/opt/dominator_analysis.h" @@ -226,9 +225,9 @@ MemPass::MemPass() {} bool MemPass::HasOnlySupportedRefs(uint32_t varId) { return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) { - auto dbg_op = user->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { return true; } SpvOp op = user->opcode(); diff --git a/source/opt/module.h b/source/opt/module.h index d609b603..43b55ae1 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -103,8 +103,8 @@ class Module { // This is due to decision by the SPIR Working Group, pending publication. inline void AddDebug3Inst(std::unique_ptr d); - // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo) - // instruction to this module. + // Appends a debug info extension (OpenCL.DebugInfo.100, + // NonSemantic.Vulkan.DebugInfo.100, or DebugInfo) instruction to this module. inline void AddExtInstDebugInfo(std::unique_ptr d); // Appends an annotation instruction to this module. @@ -192,8 +192,8 @@ class Module { inline IteratorRange debugs3() const; // Iterators for debug info instructions (excluding OpLine & OpNoLine) - // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 - // or DebugInfo extension placed between section 9 and 10. + // contained in this module. These are OpExtInst for DebugInfo extension + // placed between section 9 and 10. inline inst_iterator ext_inst_debuginfo_begin(); inline inst_iterator ext_inst_debuginfo_end(); inline IteratorRange ext_inst_debuginfo(); diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp index dd6cbbde..12a226d5 100644 --- a/source/opt/private_to_local_pass.cpp +++ b/source/opt/private_to_local_pass.cpp @@ -157,8 +157,7 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) { bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const { // The cases in this switch have to match the cases in |UpdateUse|. // If we don't know how to update it, it is not valid. - if (inst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugGlobalVariable) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { return true; } switch (inst->opcode()) { @@ -183,8 +182,7 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) { // The cases in this switch have to match the cases in |IsValidUse|. If we // don't think it is valid, the optimization will not view the variable as a // candidate, and therefore the use will not be updated. - if (inst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugGlobalVariable) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst, user); return true; diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp index c58adf47..87d2c4c0 100644 --- a/source/opt/reduce_load_size.cpp +++ b/source/opt/reduce_load_size.cpp @@ -139,7 +139,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { all_elements_used = !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) { - if (use->IsOpenCL100DebugInstr()) return true; + if (use->IsCommonDebugInstr()) return true; if (use->opcode() != SpvOpCompositeExtract || use->NumInOperands() == 1) { return false; diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index 4d47bdd8..8adca7b9 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -83,14 +83,14 @@ Pass::Status ScalarReplacementPass::ReplaceVariable( std::vector dead; bool replaced_all_uses = get_def_use_mgr()->WhileEachUser( inst, [this, &replacements, &dead](Instruction* user) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { if (ReplaceWholeDebugDeclare(user, replacements)) { dead.push_back(user); return true; } return false; } - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { if (ReplaceWholeDebugValue(user, replacements)) { dead.push_back(user); return true; @@ -504,7 +504,7 @@ void ScalarReplacementPass::CreateVariable( } } - // Update the OpenCL.DebugInfo.100 debug information. + // Update the DebugInfo debug information. inst->UpdateDebugInfoFrom(varInst); replacements->push_back(inst); @@ -791,8 +791,8 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst, get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok]( const Instruction* user, uint32_t index) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || - user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { // TODO: include num_partial_accesses if it uses Fragment operation or // DebugValue has Indexes operand. stats->num_full_accesses++; diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp index 32d6de94..5271e3fe 100644 --- a/source/opt/value_number_table.cpp +++ b/source/opt/value_number_table.cpp @@ -50,7 +50,7 @@ uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) { // OpSampledImage and OpImage must remain in the same basic block in which // they are used, because of this we will assign each one it own value number. if (!context()->IsCombinatorInstruction(inst) && - !inst->IsOpenCL100DebugInstr()) { + !inst->IsCommonDebugInstr()) { value = TakeNextValueNumber(); id_to_value_[inst->result_id()] = value; return value; diff --git a/source/opt/vector_dce.cpp b/source/opt/vector_dce.cpp index 14a55c1e..3c9f9446 100644 --- a/source/opt/vector_dce.cpp +++ b/source/opt/vector_dce.cpp @@ -52,7 +52,7 @@ void VectorDCE::FindLiveComponents(Function* function, // components are live because of arbitrary nesting of structs. function->ForEachInst( [&work_list, this, live_components](Instruction* current_inst) { - if (current_inst->IsOpenCL100DebugInstr()) { + if (current_inst->IsCommonDebugInstr()) { return; } if (!HasVectorOrScalarResult(current_inst) || @@ -394,7 +394,7 @@ void VectorDCE::MarkDebugValueUsesAsDead( Instruction* composite, std::vector* dead_dbg_value) { context()->get_def_use_mgr()->ForEachUser( composite, [&dead_dbg_value](Instruction* use) { - if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) + if (use->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) dead_dbg_value->push_back(use); }); } -- cgit v1.2.3 From 2a7a561c0c393877bd359cf0ce85792ee0e73239 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 22 Jul 2021 15:23:22 -0400 Subject: Fix local size hint id tests (#4400) * Fix tests for updated grammar * LocalSizeHintId should have three operands, not just 1 * Update deps --- DEPS | 2 +- test/binary_to_text_test.cpp | 2 +- test/val/val_modes_test.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DEPS b/DEPS index 718927d0..c4c41f0f 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382', 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683', - 'spirv_headers_revision': 'ddf3230c14c71e81fc0eae9b781cc4bcc2d1f0f5', + 'spirv_headers_revision': 'cf653e4ca4858583802b0d1656bc934edff6bd7f', } deps = { diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp index 9cad9661..df703e5d 100644 --- a/test/binary_to_text_test.cpp +++ b/test/binary_to_text_test.cpp @@ -386,7 +386,7 @@ INSTANTIATE_TEST_SUITE_P( ::testing::ValuesIn(std::vector{ "OpExecutionModeId %1 SubgroupsPerWorkgroupId %2\n", "OpExecutionModeId %1 LocalSizeId %2 %3 %4\n", - "OpExecutionModeId %1 LocalSizeHintId %2\n", + "OpExecutionModeId %1 LocalSizeHintId %2 %3 %4\n", "OpDecorateId %1 AlignmentId %2\n", "OpDecorateId %1 MaxByteOffsetId %2\n", }))); diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 99f5c9cf..d060bb79 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -672,7 +672,7 @@ INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution, Values("Kernel"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", - "LocalSizeHintId %int1"), + "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( @@ -684,7 +684,7 @@ INSTANTIATE_TEST_SUITE_P( Values("Geometry", "TessellationControl", "TessellationEvaluation", "GLCompute", "Vertex", "Fragment"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", - "LocalSizeHintId %int1"), + "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( @@ -863,7 +863,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" -OpExecutionMode %main LocalSizeHintId %int_1 +OpExecutionMode %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; @@ -882,7 +882,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" -OpExecutionModeId %main LocalSizeHintId %int_1 +OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; @@ -898,7 +898,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" -OpExecutionModeId %main LocalSizeHintId %int_1 +OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_ptr = OpTypePointer Private %int %int_1 = OpVariable %int_ptr Private -- cgit v1.2.3 From 2419f3be8667c1ff4d786858cd8761122dc7d827 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 22 Jul 2021 21:34:11 +0100 Subject: spirv-fuzz: Tighten checks on null and undef pointers (#4367) Adaps the transformations that add OpConstantUndef and OpConstantNull to a module so that pointer undefs are not allowed, and null pointers are only allowed if suitable capabilities are present. Fixes #4357. --- source/fuzz/fuzzer_util.cpp | 37 +++++++++++++++++++--- source/fuzz/fuzzer_util.h | 6 ++-- source/fuzz/transformation_add_constant_null.cpp | 4 +-- source/fuzz/transformation_add_global_undef.cpp | 9 ++++-- source/fuzz/transformation_add_global_undef.h | 2 +- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 2 ++ test/fuzz/fuzzerutil_test.cpp | 14 -------- test/fuzz/transformation_access_chain_test.cpp | 13 ++------ test/fuzz/transformation_add_copy_memory_test.cpp | 10 ++---- test/fuzz/transformation_add_synonym_test.cpp | 10 ++---- test/fuzz/transformation_load_test.cpp | 8 ++--- test/fuzz/transformation_mutate_pointer_test.cpp | 8 ++--- ...ransformation_push_id_through_variable_test.cpp | 10 +++--- test/fuzz/transformation_store_test.cpp | 4 +-- 14 files changed, 64 insertions(+), 73 deletions(-) diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index eaa62255..0e718e15 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -787,11 +787,38 @@ uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, return absolute_index - inst.NumOperands() + inst.NumInOperands(); } -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(); +bool IsNullConstantSupported(opt::IRContext* ir_context, + const opt::Instruction& type_inst) { + switch (type_inst.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeDeviceEvent: + case SpvOpTypeEvent: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeQueue: + case SpvOpTypeReserveId: + case SpvOpTypeVector: + case SpvOpTypeStruct: + return true; + case SpvOpTypePointer: + // Null pointers are allowed if the VariablePointers capability is + // enabled, or if the VariablePointersStorageBuffer capability is enabled + // and the pointer type has StorageBuffer as its storage class. + if (ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) { + return true; + } + if (ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)) { + return type_inst.GetSingleWordInOperand(0) == + SpvStorageClassStorageBuffer; + } + return false; + default: + return false; + } } bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index b956507a..a3946617 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -273,8 +273,10 @@ uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, uint32_t absolute_index); // 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); +// to have an OpConstantNull value. This may depend on the capabilities declared +// in |context|. +bool IsNullConstantSupported(opt::IRContext* context, + const opt::Instruction& type); // 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 diff --git a/source/fuzz/transformation_add_constant_null.cpp b/source/fuzz/transformation_add_constant_null.cpp index 32544e6d..c0f73670 100644 --- a/source/fuzz/transformation_add_constant_null.cpp +++ b/source/fuzz/transformation_add_constant_null.cpp @@ -35,14 +35,14 @@ bool TransformationAddConstantNull::IsApplicable( if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { return false; } - auto type = context->get_type_mgr()->GetType(message_.type_id()); + auto type = context->get_def_use_mgr()->GetDef(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); + return fuzzerutil::IsNullConstantSupported(context, *type); } void TransformationAddConstantNull::Apply( diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp index eb390ea0..ec0574a4 100644 --- a/source/fuzz/transformation_add_global_undef.cpp +++ b/source/fuzz/transformation_add_global_undef.cpp @@ -15,6 +15,7 @@ #include "source/fuzz/transformation_add_global_undef.h" #include "source/fuzz/fuzzer_util.h" +#include "source/opt/reflect.h" namespace spvtools { namespace fuzz { @@ -35,9 +36,11 @@ bool TransformationAddGlobalUndef::IsApplicable( if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } - 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(); + auto type = ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + // The type must exist, and must not be a function or pointer type. + return type != nullptr && opt::IsTypeInst(type->opcode()) && + type->opcode() != SpvOpTypeFunction && + type->opcode() != SpvOpTypePointer; } void TransformationAddGlobalUndef::Apply( diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h index 37542c3f..fff1ad9d 100644 --- a/source/fuzz/transformation_add_global_undef.h +++ b/source/fuzz/transformation_add_global_undef.h @@ -31,7 +31,7 @@ class TransformationAddGlobalUndef : public Transformation { TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id); // - |message_.fresh_id| must be fresh - // - |message_.type_id| must be the id of a non-function type + // - |message_.type_id| must be the id of a non-function, non-pointer type bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index f11885d4..a8e4acee 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -531,6 +531,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { std::string recipient_shader = R"( OpCapability Shader OpCapability ImageQuery + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -548,6 +549,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { std::string donor_shader = R"( OpCapability Shader OpCapability ImageQuery + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp index 7ff9b4d8..6771c404 100644 --- a/test/fuzz/fuzzerutil_test.cpp +++ b/test/fuzz/fuzzerutil_test.cpp @@ -196,8 +196,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolTypeTest) { %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 @@ -507,8 +505,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatTypeTest) { %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 @@ -799,8 +795,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerTypeTest) { %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 @@ -894,8 +888,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) { %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 @@ -1165,8 +1157,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetStructTypeTest) { %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 @@ -1261,8 +1251,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetVectorTypeTest) { %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 @@ -1362,8 +1350,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetVoidTypeTest) { %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 diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp index e7919816..bddcf5fe 100644 --- a/test/fuzz/transformation_access_chain_test.cpp +++ b/test/fuzz/transformation_access_chain_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationAccessChainTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %48 %54 @@ -63,7 +64,6 @@ TEST(TransformationAccessChainTest, BasicTest) { %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 @@ -203,15 +203,6 @@ TEST(TransformationAccessChainTest, BasicTest) { #ifndef NDEBUG // Bad: pointer is null - ASSERT_DEATH( - TransformationAccessChain(100, 45, {80}, - MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), transformation_context), - "Access chains should not be created from null/undefined pointers"); -#endif - -#ifndef NDEBUG - // Bad: pointer is undef ASSERT_DEATH( TransformationAccessChain(100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) @@ -331,6 +322,7 @@ TEST(TransformationAccessChainTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %48 %54 @@ -368,7 +360,6 @@ TEST(TransformationAccessChainTest, BasicTest) { %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 diff --git a/test/fuzz/transformation_add_copy_memory_test.cpp b/test/fuzz/transformation_add_copy_memory_test.cpp index 642a5561..ff8ac72e 100644 --- a/test/fuzz/transformation_add_copy_memory_test.cpp +++ b/test/fuzz/transformation_add_copy_memory_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationAddCopyMemoryTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -64,7 +65,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { %67 = OpTypePointer Function %66 %83 = OpTypePointer Private %66 %86 = OpVariable %79 Private %20 - %87 = OpUndef %79 %88 = OpConstantNull %79 %4 = OpFunction %2 None %3 %5 = OpLabel @@ -182,12 +182,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { 90, 40, SpvStorageClassPrivate, 0) .IsApplicable(context.get(), transformation_context)); - // Source instruction is OpUndef. - ASSERT_FALSE( - TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), - 90, 87, SpvStorageClassPrivate, 0) - .IsApplicable(context.get(), transformation_context)); - // Source instruction is OpConstantNull. ASSERT_FALSE( TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), @@ -255,6 +249,7 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { std::string expected = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -293,7 +288,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { %67 = OpTypePointer Function %66 %83 = OpTypePointer Private %66 %86 = OpVariable %79 Private %20 - %87 = OpUndef %79 %88 = OpConstantNull %79 %90 = OpVariable %79 Private %20 %92 = OpVariable %78 Private %25 diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp index 314b0039..ffcf1c9b 100644 --- a/test/fuzz/transformation_add_synonym_test.cpp +++ b/test/fuzz/transformation_add_synonym_test.cpp @@ -1242,9 +1242,10 @@ TEST(TransformationAddSynonymTest, MiscellaneousCopies) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } -TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { +TEST(TransformationAddSynonymTest, DoNotCopyNullPointers) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -1255,7 +1256,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %8 = OpConstantNull %7 - %9 = OpUndef %7 %4 = OpFunction %2 None %3 %5 = OpLabel OpReturn @@ -1275,12 +1275,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, MakeInstructionDescriptor(5, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); - - // Illegal to copy an OpUndef of pointer type. - ASSERT_FALSE(TransformationAddSynonym( - 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, - MakeInstructionDescriptor(5, SpvOpReturn, 0)) - .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) { diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp index a03ffdd6..3b6587f4 100644 --- a/test/fuzz/transformation_load_test.cpp +++ b/test/fuzz/transformation_load_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationLoadTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -49,7 +50,6 @@ TEST(TransformationLoadTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %4 = OpFunction %2 None %3 @@ -171,10 +171,6 @@ TEST(TransformationLoadTest, BasicTest) { 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) .IsApplicable(context.get(), transformation_context)); - // Bad: attempt to load from undefined pointer - ASSERT_FALSE(TransformationLoad( - 100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); // Bad: %40 is not available at the program point ASSERT_FALSE( TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0)) @@ -231,6 +227,7 @@ TEST(TransformationLoadTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -254,7 +251,6 @@ TEST(TransformationLoadTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %4 = OpFunction %2 None %3 diff --git a/test/fuzz/transformation_mutate_pointer_test.cpp b/test/fuzz/transformation_mutate_pointer_test.cpp index ae423100..e869efa6 100644 --- a/test/fuzz/transformation_mutate_pointer_test.cpp +++ b/test/fuzz/transformation_mutate_pointer_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationMutatePointerTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -60,7 +61,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { %23 = OpTypePointer Output %6 %24 = OpVariable %23 Output %27 = OpTypeFunction %2 %13 - %32 = OpUndef %16 %33 = OpConstantNull %16 %4 = OpFunction %2 None %3 %5 = OpLabel @@ -110,10 +110,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { ASSERT_FALSE(TransformationMutatePointer(11, 70, insert_before) .IsApplicable(context.get(), transformation_context)); - // |pointer_id| is a result id of OpUndef. - ASSERT_FALSE(TransformationMutatePointer(32, 70, insert_before) - .IsApplicable(context.get(), transformation_context)); - // |pointer_id| is a result id of OpConstantNull. ASSERT_FALSE(TransformationMutatePointer(33, 70, insert_before) .IsApplicable(context.get(), transformation_context)); @@ -163,6 +159,7 @@ TEST(TransformationMutatePointerTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -197,7 +194,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { %23 = OpTypePointer Output %6 %24 = OpVariable %23 Output %27 = OpTypeFunction %2 %13 - %32 = OpUndef %16 %33 = OpConstantNull %16 %4 = OpFunction %2 None %3 %5 = OpLabel diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp index 7487cab9..b0fff588 100644 --- a/test/fuzz/transformation_push_id_through_variable_test.cpp +++ b/test/fuzz/transformation_push_id_through_variable_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationPushIdThroughVariableTest, IsApplicable) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -50,7 +51,6 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -257,6 +257,7 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) { TEST(TransformationPushIdThroughVariableTest, Apply) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -281,7 +282,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -419,6 +419,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { std::string variant_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111 @@ -443,7 +444,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -519,6 +519,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -543,7 +544,6 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -620,6 +620,7 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -644,7 +645,6 @@ TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp index 93257d0d..ec24a73f 100644 --- a/test/fuzz/transformation_store_test.cpp +++ b/test/fuzz/transformation_store_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationStoreTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -50,7 +51,6 @@ TEST(TransformationStoreTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -290,6 +290,7 @@ TEST(TransformationStoreTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -314,7 +315,6 @@ TEST(TransformationStoreTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 -- cgit v1.2.3 From cc3fe2b67bc5eccd8df32ccf59e6149f01cb7bad Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 22 Jul 2021 23:43:45 +0100 Subject: spirv-fuzz: Fix vector wrapping fuzzer pass (#4392) The fuzzer pass was passing the type of a scalar where a vector type was required, and was not checking whether synonyms could be made for the operands to the scalar instruction. --- source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp | 32 ++++++++++++++++------ source/fuzz/fuzzer_util.cpp | 2 +- source/fuzz/fuzzer_util.h | 2 +- source/fuzz/transformation_wrap_vector_synonym.cpp | 9 ++++-- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp index 3f324df8..5f1ae18e 100644 --- a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -48,13 +48,6 @@ void FuzzerPassWrapVectorSynonym::Apply() { return; } - // The transformation will not be applicable if the id of the scalar - // operation is irrelevant. - if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( - instruction_iterator->result_id())) { - return; - } - // It must be valid to insert an OpCompositeConstruct instruction // before |instruction_iterator|. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( @@ -77,6 +70,25 @@ void FuzzerPassWrapVectorSynonym::Apply() { uint32_t target_id1 = instruction_iterator->GetSingleWordInOperand(0); uint32_t target_id2 = instruction_iterator->GetSingleWordInOperand(1); + // We need to be able to make a synonym of the scalar operation's result + // id, as well as the operand ids (for example, they cannot be + // irrelevant). + if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(), + *GetTransformationContext(), + &*instruction_iterator)) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), + GetIRContext()->get_def_use_mgr()->GetDef(target_id1))) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), + GetIRContext()->get_def_use_mgr()->GetDef(target_id2))) { + return; + } + // Stores the ids of scalar constants. std::vector vec1_components; std::vector vec2_components; @@ -95,19 +107,21 @@ void FuzzerPassWrapVectorSynonym::Apply() { } // Add two OpCompositeConstruct to the module with result id returned. + const uint32_t vector_type_id = + FindOrCreateVectorType(operand_type_id, vector_size); // Add the first OpCompositeConstruct that wraps the id of the first // operand. uint32_t result_id1 = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationCompositeConstruct( - operand_type_id, vec1_components, instruction_descriptor, + vector_type_id, vec1_components, instruction_descriptor, result_id1)); // Add the second OpCompositeConstruct that wraps the id of the second // operand. uint32_t result_id2 = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationCompositeConstruct( - operand_type_id, vec2_components, instruction_descriptor, + vector_type_id, vec2_components, instruction_descriptor, result_id2)); // Apply transformation to do vector operation and add synonym between diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 0e718e15..cae87a32 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -305,7 +305,7 @@ bool CanInsertOpcodeBeforeInstruction( bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - opt::Instruction* inst) { + const 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 diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index a3946617..40b51d55 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -118,7 +118,7 @@ bool CanInsertOpcodeBeforeInstruction( // does not participate in IdIsIrrelevant fact. bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - opt::Instruction* inst); + const opt::Instruction* inst); // Determines whether the given type is a composite; that is: an array, matrix, // struct or vector. diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp index 86eb51c8..4402589c 100644 --- a/source/fuzz/transformation_wrap_vector_synonym.cpp +++ b/source/fuzz/transformation_wrap_vector_synonym.cpp @@ -55,9 +55,12 @@ bool TransformationWrapVectorSynonym::IsApplicable( return false; } - assert(!transformation_context.GetFactManager()->IdIsIrrelevant( - instruction->result_id()) && - "Result id of the scalar operation must be relevant."); + // It must be possible to make a synonym of the result id of the scalar + // operation + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + instruction)) { + return false; + } // |vector_operand1| and |vector_operand2| must exist. auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1()); -- cgit v1.2.3 From 94bcae134408bbf132d99456179d7d29104299e5 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 22 Jul 2021 23:44:03 +0100 Subject: spirv-fuzz: Avoid out-of-bounds access (#4395) Fixes #4389. --- source/fuzz/fuzzer_pass_add_local_variables.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp index a50b0b07..03a936ba 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -34,6 +34,10 @@ void FuzzerPassAddLocalVariables::Apply() { // These are the basic types that are available to this fuzzer pass. auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // The pass cannot do anything if there are no basic types. + return; + } // 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 -- cgit v1.2.3 From 183fb9fe4c79f7ada21b61e7cf3505613e44eb4d Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 22 Jul 2021 23:44:29 +0100 Subject: spirv-fuzz: Fix problem with instruction context (#4394) Adds an additional validity check to ensure that every instruction's context pointer matches the enclosing IR context. Avoids a redundant copy constructor call in TransformationDuplicateRegionWithSelection that was leading to a bad IR context for some instructions. Related: #4387, #4388. Fixes #4393. --- source/fuzz/fuzzer_util.cpp | 6 +++++- source/fuzz/transformation_duplicate_region_with_selection.cpp | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index cae87a32..eaba94ef 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -502,8 +502,12 @@ bool IsValidAndWellFormed(const opt::IRContext* ir_context, // this is a useful aid to debugging. std::unordered_map unique_ids; bool found_duplicate = false; - ir_context->module()->ForEachInst([&consumer, &found_duplicate, + ir_context->module()->ForEachInst([&consumer, &found_duplicate, ir_context, &unique_ids](opt::Instruction* inst) { + (void)ir_context; // Only used in an assertion; keep release-mode compilers + // happy. + assert(inst->context() == ir_context && + "Instruction has wrong IR context."); if (unique_ids.count(inst->unique_id()) != 0) { consumer(SPV_MSG_INFO, nullptr, {}, "Two instructions have the same unique id (set a breakpoint to " diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp index dee1207f..a80becd9 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.cpp +++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -309,9 +309,9 @@ void TransformationDuplicateRegionWithSelection::Apply( // Construct the merge block. std::unique_ptr merge_block = - MakeUnique(MakeUnique(opt::Instruction( + MakeUnique(MakeUnique( ir_context, SpvOpLabel, 0, message_.merge_label_fresh_id(), - opt::Instruction::OperandList()))); + opt::Instruction::OperandList())); // Get the maps from the protobuf. std::map original_label_to_duplicate_label = -- cgit v1.2.3 From 63238d4f2ad3003c6a962b4d8ffb70a32716781b Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Fri, 23 Jul 2021 12:09:51 +0300 Subject: Initialize context in `opt::Instruction`'s move constructor (#4397) Fixes #4396. --- source/opt/instruction.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index e07fd624..577c0a67 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -66,7 +66,8 @@ Instruction::Instruction(IRContext* c, SpvOp op) Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, std::vector&& dbg_line) - : context_(c), + : utils::IntrusiveNodeBase(), + context_(c), opcode_(static_cast(inst.opcode)), has_type_id_(inst.type_id != 0), has_result_id_(inst.result_id != 0), @@ -86,7 +87,8 @@ 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), + : utils::IntrusiveNodeBase(), + context_(c), opcode_(static_cast(inst.opcode)), has_type_id_(inst.type_id != 0), has_result_id_(inst.result_id != 0), @@ -124,6 +126,7 @@ Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id, Instruction::Instruction(Instruction&& that) : utils::IntrusiveNodeBase(), + context_(that.context_), opcode_(that.opcode_), has_type_id_(that.has_type_id_), has_result_id_(that.has_result_id_), @@ -137,6 +140,7 @@ Instruction::Instruction(Instruction&& that) } Instruction& Instruction::operator=(Instruction&& that) { + context_ = that.context_; opcode_ = that.opcode_; has_type_id_ = that.has_type_id_; has_result_id_ = that.has_result_id_; -- cgit v1.2.3 From b2db20a7e8bf10f9da176dccf58b9c7aaaec2ccd Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Sun, 25 Jul 2021 19:23:16 +0200 Subject: BUILD.gn: introduce finer grained internal targets for Tint (#4399) Tint reaches out into SPIRV-Tools BUILD.gn file for some targets: - Adds spvtools_include_gen_dirs so that Tint can add $target_gen_dir and find autogenerated files. - Adds spvtools_language_headers that Tint can reference so it automatically picks up new language headers that are added. --- BUILD.gn | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 791d7a35..fdf45847 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -358,14 +358,20 @@ config("spvtools_public_config") { include_dirs = [ "include" ] } +config("spvtools_include_gen_dirs") { + include_dirs = [ "$target_gen_dir" ] +} + config("spvtools_internal_config") { include_dirs = [ ".", - "$target_gen_dir", "${spirv_headers}/include", ] - configs = [ ":spvtools_public_config" ] + configs = [ + ":spvtools_public_config", + ":spvtools_include_gen_dirs", + ] cflags = [] if (is_clang) { @@ -391,6 +397,14 @@ source_set("spvtools_headers") { public_configs = [ ":spvtools_public_config" ] } +group("spvtools_language_headers") { + public_deps = [ + ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", + ] +} + static_library("spvtools") { deps = [ ":spvtools_core_tables_unified1", -- cgit v1.2.3 From 4376a10c1d917aae2f26908e2123ff5597292362 Mon Sep 17 00:00:00 2001 From: krockot Date: Mon, 26 Jul 2021 11:27:25 -0700 Subject: Fix public deps on generated headers (#4386) Some generated headers are exposed by headers in the spvtools_opt target, but its dependency on them is private. This can result in build flake, since the headers don't need to be generated before compiling any spvtools_opt dependents. This fixes the build flake by correctly expressing these as public dependencies. --- BUILD.gn | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index fdf45847..32a44ff0 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -786,12 +786,14 @@ static_library("spvtools_opt") { deps = [ ":spvtools", - ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", - ":spvtools_language_header_vkdebuginfo100", ":spvtools_vendor_tables_spv-amd-shader-ballot", ] - public_deps = [ ":spvtools_headers" ] + public_deps = [ + ":spvtools_headers", + ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_vkdebuginfo100", + ] if (build_with_chromium) { configs -= [ "//build/config/compiler:chromium_code" ] -- cgit v1.2.3 From 11cd875ed88484f93943071083b4821b4c3d2193 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 27 Jul 2021 13:18:36 +0100 Subject: spirv-fuzz: Use reference in CanMakeSynonymOf (#4401) The instruction parameter of CanMakeSynonymOf is an input parameter that should never be null, so a const reference is a more appropriate type than a const pointer. --- .../fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp | 2 +- source/fuzz/fuzzer_pass_construct_composites.cpp | 2 +- source/fuzz/fuzzer_pass_expand_vector_reductions.cpp | 2 +- source/fuzz/fuzzer_pass_push_ids_through_variables.cpp | 2 +- source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp | 6 +++--- source/fuzz/fuzzer_util.cpp | 16 ++++++++-------- source/fuzz/fuzzer_util.h | 2 +- source/fuzz/transformation_add_synonym.cpp | 3 ++- source/fuzz/transformation_composite_construct.cpp | 2 +- source/fuzz/transformation_composite_extract.cpp | 2 +- source/fuzz/transformation_composite_insert.cpp | 8 ++++---- source/fuzz/transformation_expand_vector_reduction.cpp | 2 +- source/fuzz/transformation_push_id_through_variable.cpp | 2 +- source/fuzz/transformation_vector_shuffle.cpp | 4 ++-- source/fuzz/transformation_wrap_vector_synonym.cpp | 2 +- 15 files changed, 29 insertions(+), 28 deletions(-) diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp index e2eaaa0a..0275607f 100644 --- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -75,7 +75,7 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() { ->IdIsIrrelevant(instruction->result_id()) && !fuzzerutil::CanMakeSynonymOf(ir_context, *GetTransformationContext(), - instruction)) { + *instruction)) { // If the id is irrelevant, we can use it since it will not // participate in DataSynonym fact. Otherwise, we should be // able to produce a synonym out of the id. diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index 1a174cf1..bc78baed 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -61,7 +61,7 @@ void FuzzerPassConstructComposites::Apply() { return GetTransformationContext()->GetFactManager()->IdIsIrrelevant( inst->result_id()) || fuzzerutil::CanMakeSynonymOf(ir_context, - *GetTransformationContext(), inst); + *GetTransformationContext(), *inst); }); ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp index e25dcbc3..2dcfff1a 100644 --- a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp +++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp @@ -46,7 +46,7 @@ void FuzzerPassExpandVectorReductions::Apply() { // It must be able to make a synonym of |instruction|. if (!fuzzerutil::CanMakeSynonymOf( - GetIRContext(), *GetTransformationContext(), &instruction)) { + GetIRContext(), *GetTransformationContext(), instruction)) { continue; } diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 6aebac66..40576564 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -102,7 +102,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() { ->IdIsIrrelevant(instruction->result_id()) && !fuzzerutil::CanMakeSynonymOf(ir_context, *GetTransformationContext(), - instruction)) { + *instruction)) { return false; } diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp index 5f1ae18e..ff917cd6 100644 --- a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -75,17 +75,17 @@ void FuzzerPassWrapVectorSynonym::Apply() { // irrelevant). if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(), *GetTransformationContext(), - &*instruction_iterator)) { + *instruction_iterator)) { return; } if (!fuzzerutil::CanMakeSynonymOf( GetIRContext(), *GetTransformationContext(), - GetIRContext()->get_def_use_mgr()->GetDef(target_id1))) { + *GetIRContext()->get_def_use_mgr()->GetDef(target_id1))) { return; } if (!fuzzerutil::CanMakeSynonymOf( GetIRContext(), *GetTransformationContext(), - GetIRContext()->get_def_use_mgr()->GetDef(target_id2))) { + *GetIRContext()->get_def_use_mgr()->GetDef(target_id2))) { return; } diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index eaba94ef..3ab24ad6 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -305,34 +305,34 @@ bool CanInsertOpcodeBeforeInstruction( bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - const opt::Instruction* inst) { - if (inst->opcode() == SpvOpSampledImage) { + const 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()) { + if (!inst.HasResultId()) { // We can only make a synonym of an instruction that generates an id. return false; } if (transformation_context.GetFactManager()->IdIsIrrelevant( - inst->result_id())) { + inst.result_id())) { // An irrelevant id can't be a synonym of anything. return false; } - if (!inst->type_id()) { + if (!inst.type_id()) { // 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()); + auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst.type_id()); if (type_inst->opcode() == SpvOpTypeVoid) { // We only make synonyms of instructions that define objects, and an object // cannot have void type. return false; } if (type_inst->opcode() == SpvOpTypePointer) { - switch (inst->opcode()) { + switch (inst.opcode()) { case SpvOpConstantNull: case SpvOpUndef: // We disallow making synonyms of null or undefined pointers. This is @@ -348,7 +348,7 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, // not decorated analogously, using the original object vs. its synonymous // form may not be equivalent. return ir_context->get_decoration_mgr() - ->GetDecorationsFor(inst->result_id(), true) + ->GetDecorationsFor(inst.result_id(), true) .empty(); } diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 40b51d55..a6c92be0 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -118,7 +118,7 @@ bool CanInsertOpcodeBeforeInstruction( // does not participate in IdIsIrrelevant fact. bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - const opt::Instruction* inst); + const opt::Instruction& inst); // Determines whether the given type is a composite; that is: an array, matrix, // struct or vector. diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp index a1949fbc..69269e5e 100644 --- a/source/fuzz/transformation_add_synonym.cpp +++ b/source/fuzz/transformation_add_synonym.cpp @@ -151,7 +151,8 @@ bool TransformationAddSynonym::IsInstructionValid( return false; } - if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) { + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + *inst)) { return false; } diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp index 0cd2308b..2d8e5991 100644 --- a/source/fuzz/transformation_composite_construct.cpp +++ b/source/fuzz/transformation_composite_construct.cpp @@ -297,7 +297,7 @@ void TransformationCompositeConstruct::AddDataSynonymFacts( composite_type->AsVector() && component_type->AsVector(); if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(component))) { + *ir_context->get_def_use_mgr()->GetDef(component))) { // We can't make a synonym of this component, so we skip on to the next // component. In the case where we're packing a vector into a vector we // have to skip as many components of the resulting vectors as there are diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp index 647cd74e..0fbd4e1b 100644 --- a/source/fuzz/transformation_composite_extract.cpp +++ b/source/fuzz/transformation_composite_extract.cpp @@ -125,7 +125,7 @@ void TransformationCompositeExtract::AddDataSynonymFacts( // or if the result id into which we are extracting is irrelevant. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || + *ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || transformation_context->GetFactManager()->IdIsIrrelevant( message_.fresh_id())) { return; diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp index 05162bfc..60fa5628 100644 --- a/source/fuzz/transformation_composite_insert.cpp +++ b/source/fuzz/transformation_composite_insert.cpp @@ -219,9 +219,9 @@ void TransformationCompositeInsert::AddDataSynonymFacts( continue; } current_index.push_back(i); - if (fuzzerutil::CanMakeSynonymOf( - ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.composite_id()))) { + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef( + message_.composite_id()))) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(message_.fresh_id(), current_index), MakeDataDescriptor(message_.composite_id(), current_index)); @@ -235,7 +235,7 @@ void TransformationCompositeInsert::AddDataSynonymFacts( // synonymous with the result of the insert instruction at the given index. if (fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(message_.object_id(), {}), MakeDataDescriptor(message_.fresh_id(), index)); diff --git a/source/fuzz/transformation_expand_vector_reduction.cpp b/source/fuzz/transformation_expand_vector_reduction.cpp index 99387066..bafcf929 100644 --- a/source/fuzz/transformation_expand_vector_reduction.cpp +++ b/source/fuzz/transformation_expand_vector_reduction.cpp @@ -129,7 +129,7 @@ void TransformationExpandVectorReduction::Apply( // If it's possible to make a synonym of |instruction|, then add the fact that // the last |logical_instruction| is a synonym of |instruction|. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, - instruction)) { + *instruction)) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(logical_instruction.result_id(), {}), MakeDataDescriptor(instruction->result_id(), {})); diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp index ff525165..55a57a15 100644 --- a/source/fuzz/transformation_push_id_through_variable.cpp +++ b/source/fuzz/transformation_push_id_through_variable.cpp @@ -155,7 +155,7 @@ void TransformationPushIdThroughVariable::Apply( // We should be able to create a synonym of |value_id| if it's not irrelevant. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, - value_instruction) && + *value_instruction) && !transformation_context->GetFactManager()->IdIsIrrelevant( message_.value_synonym_id())) { // Adds the fact that |message_.value_synonym_id| diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp index ac0e3ccb..742a2c80 100644 --- a/source/fuzz/transformation_vector_shuffle.cpp +++ b/source/fuzz/transformation_vector_shuffle.cpp @@ -204,7 +204,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts( // Check that the first vector can participate in data synonym facts. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { continue; } descriptor_for_source_component = @@ -213,7 +213,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts( // Check that the second vector can participate in data synonym facts. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { continue; } auto index_into_vector_2 = diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp index 4402589c..d0eddd44 100644 --- a/source/fuzz/transformation_wrap_vector_synonym.cpp +++ b/source/fuzz/transformation_wrap_vector_synonym.cpp @@ -58,7 +58,7 @@ bool TransformationWrapVectorSynonym::IsApplicable( // It must be possible to make a synonym of the result id of the scalar // operation if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, - instruction)) { + *instruction)) { return false; } -- cgit v1.2.3 From 7dadcf9c76a9109a5a493a416ae59c2b3c02070f Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Wed, 28 Jul 2021 11:44:32 -0500 Subject: Add control dependence analysis to opt (#4380) Control dependence analysis constructs a control dependence graph, representing the conditions for a block's execution relative to the results of other blocks with conditional branches, etc. This is an analysis pass that will be useful for the linter and potentially also useful in opt. Currently it is unused except for the added unit tests. --- Android.mk | 1 + BUILD.gn | 2 + source/opt/CMakeLists.txt | 2 + source/opt/control_dependence.cpp | 156 ++++++++++++++++++++++ source/opt/control_dependence.h | 199 ++++++++++++++++++++++++++++ test/opt/CMakeLists.txt | 1 + test/opt/control_dependence.cpp | 269 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 630 insertions(+) create mode 100644 source/opt/control_dependence.cpp create mode 100644 source/opt/control_dependence.h create mode 100644 test/opt/control_dependence.cpp diff --git a/Android.mk b/Android.mk index bdcdda9a..057731fc 100644 --- a/Android.mk +++ b/Android.mk @@ -88,6 +88,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/composite.cpp \ source/opt/const_folding_rules.cpp \ source/opt/constants.cpp \ + source/opt/control_dependence.cpp \ source/opt/convert_to_half_pass.cpp \ source/opt/copy_prop_arrays.cpp \ source/opt/dead_branch_elim_pass.cpp \ diff --git a/BUILD.gn b/BUILD.gn index 32a44ff0..fea02797 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -606,6 +606,8 @@ static_library("spvtools_opt") { "source/opt/const_folding_rules.h", "source/opt/constants.cpp", "source/opt/constants.h", + "source/opt/control_dependence.cpp", + "source/opt/control_dependence.h", "source/opt/convert_to_half_pass.cpp", "source/opt/convert_to_half_pass.h", "source/opt/copy_prop_arrays.cpp", diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 0e41b205..f6ebcfa6 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -27,6 +27,7 @@ set(SPIRV_TOOLS_OPT_SOURCES composite.h const_folding_rules.h constants.h + control_dependence.h convert_to_half_pass.h copy_prop_arrays.h dead_branch_elim_pass.h @@ -133,6 +134,7 @@ set(SPIRV_TOOLS_OPT_SOURCES composite.cpp const_folding_rules.cpp constants.cpp + control_dependence.cpp convert_to_half_pass.cpp copy_prop_arrays.cpp dead_branch_elim_pass.cpp diff --git a/source/opt/control_dependence.cpp b/source/opt/control_dependence.cpp new file mode 100644 index 00000000..f4879e0f --- /dev/null +++ b/source/opt/control_dependence.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2021 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/opt/control_dependence.h" + +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/cfg.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "spirv/unified1/spirv.h" + +// Computes the control dependence graph (CDG) using the algorithm in Cytron +// 1991, "Efficiently Computing Static Single Assignment Form and the Control +// Dependence Graph." It relies on the fact that the control dependence sources +// (blocks on which a block is control dependent) are exactly the post-dominance +// frontier for that block. The explanation and proofs are given in Section 6 of +// that paper. +// Link: https://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf +// +// The algorithm in Section 4.2 of the same paper is used to construct the +// dominance frontier. It uses the post-dominance tree, which is available in +// the IR context. + +namespace spvtools { +namespace opt { +constexpr uint32_t ControlDependenceAnalysis::kPseudoEntryBlock; + +uint32_t ControlDependence::GetConditionID(const CFG& cfg) const { + if (source_bb_id() == 0) { + // Entry dependence; return 0. + return 0; + } + const BasicBlock* source_bb = cfg.block(source_bb_id()); + const Instruction* branch = source_bb->terminator(); + assert((branch->opcode() == SpvOpBranchConditional || + branch->opcode() == SpvOpSwitch) && + "invalid control dependence; last instruction must be conditional " + "branch or switch"); + return branch->GetSingleWordInOperand(0); +} + +bool ControlDependence::operator<(const ControlDependence& other) const { + return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) < + std::tie(other.source_bb_id_, other.target_bb_id_, + other.branch_target_bb_id_); +} + +bool ControlDependence::operator==(const ControlDependence& other) const { + return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) == + std::tie(other.source_bb_id_, other.target_bb_id_, + other.branch_target_bb_id_); +} + +std::ostream& operator<<(std::ostream& os, const ControlDependence& dep) { + os << dep.source_bb_id() << "->" << dep.target_bb_id(); + if (dep.branch_target_bb_id() != dep.target_bb_id()) { + os << " through " << dep.branch_target_bb_id(); + } + return os; +} + +void ControlDependenceAnalysis::ComputePostDominanceFrontiers( + const CFG& cfg, const PostDominatorAnalysis& pdom) { + // Compute post-dominance frontiers (reverse graph). + // The dominance frontier for a block X is equal to (Equation 4) + // DF_local(X) U { B in DF_up(Z) | X = ipdom(Z) } + // (ipdom(Z) is the immediate post-dominator of Z.) + // where + // DF_local(X) = { Y | X -> Y in CFG, X does not strictly post-dominate Y } + // represents the contribution of X's predecessors to the DF, and + // DF_up(Z) = { Y | Y in DF(Z), ipdom(Z) does not strictly post-dominate Y } + // (note: ipdom(Z) = X.) + // represents the contribution of a block to its immediate post- + // dominator's DF. + // This is computed in one pass through a post-order traversal of the + // post-dominator tree. + + // Assert that there is a block other than the pseudo exit in the pdom tree, + // as we need one to get the function entry point (as the pseudo exit is not + // actually part of the function.) + assert(!cfg.IsPseudoExitBlock(pdom.GetDomTree().post_begin()->bb_)); + Function* function = pdom.GetDomTree().post_begin()->bb_->GetParent(); + uint32_t function_entry = function->entry()->id(); + // Explicitly initialize pseudo-entry block, as it doesn't depend on anything, + // so it won't be initialized in the following loop. + reverse_nodes_[kPseudoEntryBlock] = {}; + for (auto it = pdom.GetDomTree().post_cbegin(); + it != pdom.GetDomTree().post_cend(); ++it) { + ComputePostDominanceFrontierForNode(cfg, pdom, function_entry, *it); + } +} + +void ControlDependenceAnalysis::ComputePostDominanceFrontierForNode( + const CFG& cfg, const PostDominatorAnalysis& pdom, uint32_t function_entry, + const DominatorTreeNode& pdom_node) { + const uint32_t label = pdom_node.id(); + ControlDependenceList& edges = reverse_nodes_[label]; + for (uint32_t pred : cfg.preds(label)) { + if (!pdom.StrictlyDominates(label, pred)) { + edges.push_back(ControlDependence(pred, label)); + } + } + if (label == function_entry) { + // Add edge from pseudo-entry to entry. + // In CDG construction, an edge is added from entry to exit, so only the + // exit node can post-dominate entry. + edges.push_back(ControlDependence(kPseudoEntryBlock, label)); + } + for (DominatorTreeNode* child : pdom_node) { + // Note: iterate dependences by value, as we need a copy. + for (const ControlDependence& dep : reverse_nodes_[child->id()]) { + // Special-case pseudo-entry, as above. + if (dep.source_bb_id() == kPseudoEntryBlock || + !pdom.StrictlyDominates(label, dep.source_bb_id())) { + edges.push_back(ControlDependence(dep.source_bb_id(), label, + dep.branch_target_bb_id())); + } + } + } +} + +void ControlDependenceAnalysis::ComputeControlDependenceGraph( + const CFG& cfg, const PostDominatorAnalysis& pdom) { + ComputePostDominanceFrontiers(cfg, pdom); + ComputeForwardGraphFromReverse(); +} + +void ControlDependenceAnalysis::ComputeForwardGraphFromReverse() { + for (const auto& entry : reverse_nodes_) { + // Ensure an entry is created for each node. + forward_nodes_[entry.first]; + for (const ControlDependence& dep : entry.second) { + forward_nodes_[dep.source_bb_id()].push_back(dep); + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/control_dependence.h b/source/opt/control_dependence.h new file mode 100644 index 00000000..920e4dcf --- /dev/null +++ b/source/opt/control_dependence.h @@ -0,0 +1,199 @@ +// Copyright (c) 2021 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_OPT_CONTROL_DEPENDENCE_H_ +#define SOURCE_OPT_CONTROL_DEPENDENCE_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/dominator_analysis.h" + +namespace spvtools { +namespace opt { + +class ControlDependence { + public: + // The label of the source of this dependence, i.e. the block on which the + // target is dependent on. + // A |source_bb_id| of 0 represents an "entry" dependence, meaning that the + // execution of |target_bb_id| is only dependent on entry to the function. + uint32_t source_bb_id() const { return source_bb_id_; } + // The label of the target of this dependence, i.e. the block which is + // dependent on the source. + uint32_t target_bb_id() const { return target_bb_id_; } + // The label of the target of the *branch* for this dependence. + // Equal to the ID of the entry block for entry dependences. + // + // For example, for the partial CFG pictured below: + // 1 ---> 2 ---> 4 ---> 6 + // \ \ ^ + // \-> 3 \-> 5 -----/ + // Block 6 is control dependent on block 1, but this dependence comes from the + // branch 1 -> 2, so in this case the branch target ID would be 2. + uint32_t branch_target_bb_id() const { return branch_target_bb_id_; } + + // Create a direct control dependence from BB ID |source| to |target|. + ControlDependence(uint32_t source, uint32_t target) + : source_bb_id_(source), + target_bb_id_(target), + branch_target_bb_id_(target) {} + // Create a control dependence from BB ID |source| to |target| through the + // branch from |source| to |branch_target|. + ControlDependence(uint32_t source, uint32_t target, uint32_t branch_target) + : source_bb_id_(source), + target_bb_id_(target), + branch_target_bb_id_(branch_target) {} + + // Gets the ID of the conditional value for the branch corresponding to this + // control dependence. This is the first input operand for both + // OpConditionalBranch and OpSwitch. + // Returns 0 for entry dependences. + uint32_t GetConditionID(const CFG& cfg) const; + + bool operator==(const ControlDependence& other) const; + bool operator!=(const ControlDependence& other) const { + return !(*this == other); + } + + // Comparison operators, ordered lexicographically. Total ordering. + bool operator<(const ControlDependence& other) const; + bool operator>(const ControlDependence& other) const { return other < *this; } + bool operator<=(const ControlDependence& other) const { + return !(*this > other); + } + bool operator>=(const ControlDependence& other) const { + return !(*this < other); + } + + private: + uint32_t source_bb_id_; + uint32_t target_bb_id_; + uint32_t branch_target_bb_id_; +}; + +// Prints |dep| to |os| in a human-readable way. For example, +// 1->2 (target_bb_id = branch_target_bb_id = 2) +// 3->4 through 5 (target_bb_id = 4, branch_target_bb_id = 5) +std::ostream& operator<<(std::ostream& os, const ControlDependence& dep); + +// Represents the control dependence graph. A basic block is control dependent +// on another if the result of that block (e.g. the condition of a conditional +// branch) influences whether it is executed or not. More formally, a block A is +// control dependent on B iff: +// 1. there exists a path from A to the exit node that does *not* go through B +// (i.e., A does not postdominate B), and +// 2. there exists a path B -> b_1 -> ... -> b_n -> A such that A post-dominates +// all nodes b_i. +class ControlDependenceAnalysis { + public: + // Map basic block labels to control dependencies/dependents. + // Not guaranteed to be in any particular order. + using ControlDependenceList = std::vector; + using ControlDependenceListMap = + std::unordered_map; + + // 0, the label number for the pseudo entry block. + // All control dependences on the pseudo entry block are of type kEntry, and + // vice versa. + static constexpr uint32_t kPseudoEntryBlock = 0; + + // Build the control dependence graph for the given control flow graph |cfg| + // and corresponding post-dominator analysis |pdom|. + void ComputeControlDependenceGraph(const CFG& cfg, + const PostDominatorAnalysis& pdom); + + // Get the list of the nodes that depend on a block. + // Return value is not guaranteed to be in any particular order. + const ControlDependenceList& GetDependenceTargets(uint32_t block) const { + return forward_nodes_.at(block); + } + + // Get the list of the nodes on which a block depends on. + // Return value is not guaranteed to be in any particular order. + const ControlDependenceList& GetDependenceSources(uint32_t block) const { + return reverse_nodes_.at(block); + } + + // Runs the function |f| on each block label in the CDG. If any iteration + // returns false, immediately stops iteration and returns false. Otherwise + // returns true. Nodes are iterated in some undefined order, including the + // pseudo-entry block. + bool WhileEachBlockLabel(std::function f) const { + for (const auto& entry : forward_nodes_) { + if (!f(entry.first)) { + return false; + } + } + return true; + } + + // Runs the function |f| on each block label in the CDG. Nodes are iterated in + // some undefined order, including the pseudo-entry block. + void ForEachBlockLabel(std::function f) const { + WhileEachBlockLabel([&f](uint32_t label) { + f(label); + return true; + }); + } + + // Returns true if the block |id| exists in the control dependence graph. + // This can be false even if the block exists in the function when it is part + // of an infinite loop, since it is not part of the post-dominator tree. + bool DoesBlockExist(uint32_t id) const { + return forward_nodes_.count(id) > 0; + } + + // Returns true if block |a| is dependent on block |b|. + bool IsDependent(uint32_t a, uint32_t b) const { + if (!DoesBlockExist(a)) return false; + // BBs tend to have more dependents (targets) than they are dependent on + // (sources), so search sources. + const ControlDependenceList& a_sources = GetDependenceSources(a); + return std::find_if(a_sources.begin(), a_sources.end(), + [b](const ControlDependence& dep) { + return dep.source_bb_id() == b; + }) != a_sources.end(); + } + + private: + // Computes the post-dominance frontiers (i.e. the reverse CDG) for each node + // in the post-dominator tree. Only modifies reverse_nodes_; forward_nodes_ is + // not modified. + void ComputePostDominanceFrontiers(const CFG& cfg, + const PostDominatorAnalysis& pdom); + // Computes the post-dominance frontier for a specific node |pdom_node| in the + // post-dominator tree. Result is placed in reverse_nodes_[pdom_node.id()]. + void ComputePostDominanceFrontierForNode(const CFG& cfg, + const PostDominatorAnalysis& pdom, + uint32_t function_entry, + const DominatorTreeNode& pdom_node); + + // Computes the forward graph (forward_nodes_) from the reverse graph + // (reverse_nodes_). + void ComputeForwardGraphFromReverse(); + + ControlDependenceListMap forward_nodes_; + ControlDependenceListMap reverse_nodes_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONTROL_DEPENDENCE_H_ diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 621a6aa4..03312468 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -28,6 +28,7 @@ add_spvtools_unittest(TARGET opt compact_ids_test.cpp constants_test.cpp constant_manager_test.cpp + control_dependence.cpp convert_relaxed_to_half_test.cpp copy_prop_array_test.cpp dead_branch_elim_test.cpp diff --git a/test/opt/control_dependence.cpp b/test/opt/control_dependence.cpp new file mode 100644 index 00000000..02793f1e --- /dev/null +++ b/test/opt/control_dependence.cpp @@ -0,0 +1,269 @@ +// Copyright (c) 2021 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/opt/control_dependence.h" + +#include +#include + +#include "gmock/gmock-matchers.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/cfg.h" +#include "test/opt/function_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +void GatherEdges(const ControlDependenceAnalysis& cdg, + std::vector& ret) { + cdg.ForEachBlockLabel([&](uint32_t label) { + ret.reserve(ret.size() + cdg.GetDependenceTargets(label).size()); + ret.insert(ret.end(), cdg.GetDependenceTargets(label).begin(), + cdg.GetDependenceTargets(label).end()); + }); + std::sort(ret.begin(), ret.end()); + // Verify that reverse graph is the same. + std::vector reverse_edges; + reverse_edges.reserve(ret.size()); + cdg.ForEachBlockLabel([&](uint32_t label) { + reverse_edges.insert(reverse_edges.end(), + cdg.GetDependenceSources(label).begin(), + cdg.GetDependenceSources(label).end()); + }); + std::sort(reverse_edges.begin(), reverse_edges.end()); + ASSERT_THAT(reverse_edges, testing::ElementsAreArray(ret)); +} + +using ControlDependenceTest = ::testing::Test; + +TEST(ControlDependenceTest, DependenceSimpleCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstant %5 0 + %7 = OpConstantFalse %4 + %8 = OpConstantTrue %4 + %9 = OpConstant %5 1 + %1 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSwitch %6 %12 1 %13 + %12 = OpLabel + OpBranch %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranchConditional %8 %15 %16 + %15 = OpLabel + OpBranch %19 + %16 = OpLabel + OpBranchConditional %8 %17 %18 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // CFG: (all edges pointing downward) + // %10 + // | + // %11 + // / \ (R: %6 == 1, L: default) + // %12 %13 + // \ / + // %14 + // T/ \F + // %15 %16 + // | T/ |F + // | %17| + // | \ | + // | %18 + // | / + // %19 + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, 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* fn = spvtest::GetFunction(module, 1); + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + { + PostDominatorAnalysis pdom; + const CFG& cfg = *context->cfg(); + pdom.InitializeTree(cfg, fn); + ControlDependenceAnalysis cdg; + cdg.ComputeControlDependenceGraph(cfg, pdom); + + EXPECT_TRUE(cdg.IsDependent(12, 11)); + EXPECT_TRUE(cdg.IsDependent(13, 11)); + EXPECT_TRUE(cdg.IsDependent(15, 14)); + EXPECT_TRUE(cdg.IsDependent(16, 14)); + EXPECT_TRUE(cdg.IsDependent(18, 14)); + EXPECT_TRUE(cdg.IsDependent(17, 16)); + EXPECT_TRUE(cdg.IsDependent(10, 0)); + EXPECT_TRUE(cdg.IsDependent(11, 0)); + EXPECT_TRUE(cdg.IsDependent(14, 0)); + EXPECT_TRUE(cdg.IsDependent(19, 0)); + EXPECT_FALSE(cdg.IsDependent(14, 11)); + EXPECT_FALSE(cdg.IsDependent(17, 14)); + EXPECT_FALSE(cdg.IsDependent(19, 14)); + EXPECT_FALSE(cdg.IsDependent(12, 0)); + + std::vector edges; + GatherEdges(cdg, edges); + EXPECT_THAT(edges, + testing::ElementsAre( + ControlDependence(0, 10), ControlDependence(0, 11, 10), + ControlDependence(0, 14, 10), ControlDependence(0, 19, 10), + ControlDependence(11, 12), ControlDependence(11, 13), + ControlDependence(14, 15), ControlDependence(14, 16), + ControlDependence(14, 18, 16), ControlDependence(16, 17))); + + const uint32_t expected_condition_ids[] = { + 0, 0, 0, 0, 6, 6, 8, 8, 8, 8, + }; + + for (uint32_t i = 0; i < edges.size(); i++) { + EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); + } + } +} + +TEST(ControlDependenceTest, DependencePaperCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %101 "main" + %102 = OpTypeVoid + %103 = OpTypeFunction %102 + %104 = OpTypeBool + %108 = OpConstantTrue %104 + %101 = OpFunction %102 None %103 + %1 = OpLabel + OpBranch %2 + %2 = OpLabel + OpBranchConditional %108 %3 %7 + %3 = OpLabel + OpBranchConditional %108 %4 %5 + %4 = OpLabel + OpBranch %6 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranchConditional %108 %10 %11 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %108 %12 %9 + %12 = OpLabel + OpBranchConditional %108 %13 %2 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // CFG: (edges pointing downward if no arrow) + // %1 + // | + // %2 <----+ + // T/ \F | + // %3 \ | + // T/ \F \ | + // %4 %5 %7 | + // \ / / | + // %6 / | + // \ / | + // %8 | + // | | + // %9 <-+ | + // T/ | | | + // %10 | | | + // \ | | | + // %11-F+ | + // T| | + // %12-F---+ + // T| + // %13 + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, 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* fn = spvtest::GetFunction(module, 101); + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 1); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + { + PostDominatorAnalysis pdom; + const CFG& cfg = *context->cfg(); + pdom.InitializeTree(cfg, fn); + ControlDependenceAnalysis cdg; + cdg.ComputeControlDependenceGraph(cfg, pdom); + + std::vector edges; + GatherEdges(cdg, edges); + EXPECT_THAT( + edges, testing::ElementsAre( + ControlDependence(0, 1), ControlDependence(0, 2, 1), + ControlDependence(0, 8, 1), ControlDependence(0, 9, 1), + ControlDependence(0, 11, 1), ControlDependence(0, 12, 1), + ControlDependence(0, 13, 1), ControlDependence(2, 3), + ControlDependence(2, 6, 3), ControlDependence(2, 7), + ControlDependence(3, 4), ControlDependence(3, 5), + ControlDependence(9, 10), ControlDependence(11, 9), + ControlDependence(11, 11, 9), ControlDependence(12, 2), + ControlDependence(12, 8, 2), ControlDependence(12, 9, 2), + ControlDependence(12, 11, 2), ControlDependence(12, 12, 2))); + + const uint32_t expected_condition_ids[] = { + 0, 0, 0, 0, 0, 0, 0, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + }; + + for (uint32_t i = 0; i < edges.size(); i++) { + EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); + } + } +} + +} // namespace +} // namespace opt +} // namespace spvtools -- cgit v1.2.3 From 4bcd13ff130aa4d3defc6351a875609a05211d5e Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Wed, 28 Jul 2021 15:56:51 -0500 Subject: spirv-opt: Add more tests to control dependence (#4410) In particular, adds tests for DoesBlockExist, ForEachBlockLabel, and WhileEachBlockLabel. --- test/opt/control_dependence.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/opt/control_dependence.cpp b/test/opt/control_dependence.cpp index 02793f1e..94618d7e 100644 --- a/test/opt/control_dependence.cpp +++ b/test/opt/control_dependence.cpp @@ -122,6 +122,43 @@ TEST(ControlDependenceTest, DependenceSimpleCFG) { ControlDependenceAnalysis cdg; cdg.ComputeControlDependenceGraph(cfg, pdom); + // Test DoesBlockExist. + for (uint32_t id = 10; id <= 19; id++) { + EXPECT_TRUE(cdg.DoesBlockExist(id)); + } + EXPECT_TRUE( + cdg.DoesBlockExist(ControlDependenceAnalysis::kPseudoEntryBlock)); + // Check blocks before/after valid range. + EXPECT_FALSE(cdg.DoesBlockExist(5)); + EXPECT_FALSE(cdg.DoesBlockExist(25)); + EXPECT_FALSE(cdg.DoesBlockExist(UINT32_MAX)); + + // Test ForEachBlockLabel. + std::set block_labels; + cdg.ForEachBlockLabel([&block_labels](uint32_t id) { + bool inserted = block_labels.insert(id).second; + EXPECT_TRUE(inserted); // Should have no duplicates. + }); + EXPECT_THAT(block_labels, testing::ElementsAre(0, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19)); + + { + // Test WhileEachBlockLabel. + uint32_t iters = 0; + EXPECT_TRUE(cdg.WhileEachBlockLabel([&iters](uint32_t) { + ++iters; + return true; + })); + EXPECT_EQ((uint32_t)block_labels.size(), iters); + iters = 0; + EXPECT_FALSE(cdg.WhileEachBlockLabel([&iters](uint32_t) { + ++iters; + return false; + })); + EXPECT_EQ(1, iters); + } + + // Test IsDependent. EXPECT_TRUE(cdg.IsDependent(12, 11)); EXPECT_TRUE(cdg.IsDependent(13, 11)); EXPECT_TRUE(cdg.IsDependent(15, 14)); @@ -137,6 +174,7 @@ TEST(ControlDependenceTest, DependenceSimpleCFG) { EXPECT_FALSE(cdg.IsDependent(19, 14)); EXPECT_FALSE(cdg.IsDependent(12, 0)); + // Test GetDependenceSources/Targets. std::vector edges; GatherEdges(cdg, edges); EXPECT_THAT(edges, -- cgit v1.2.3 From c9e094cc4d553181db2f9c1fa4d31750668c7d13 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 28 Jul 2021 22:59:02 +0100 Subject: spirv-fuzz: Quit fuzzer pass when no types are available (#4409) The fuzzer pass that adds global variables requires some basic types. This change makes the fuzzer pass exit gracefully when none are available. Fixes #4408. --- source/fuzz/fuzzer_pass_add_global_variables.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index 06797413..be78ee52 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -47,6 +47,10 @@ void FuzzerPassAddGlobalVariables::Apply() { // These are the basic types that are available to this fuzzer pass. auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // There are no basic types, so there is nothing this fuzzer pass can do. + return; + } // 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 -- cgit v1.2.3 From 9c4481419ec1710d1b098982ecffda6b7fad88a9 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 28 Jul 2021 22:59:37 +0100 Subject: spirv-fuzz: Allow inapplicable transformations to be ignored (#4407) spirv-fuzz features transformations that should be applicable by construction. Assertions are used to detect when such transformations turn out to be inapplicable. Failures of such assertions indicate bugs in the fuzzer. However, when using the fuzzer at scale (e.g. in ClusterFuzz) reports of these assertion failures create noise, and cause the fuzzer to exit early. This change adds an option whereby inapplicable transformations can be ignored. This reduces noise and allows fuzzing to continue even when a transformation that should be applicable but is not has been erroneously created. --- source/fuzz/fuzzer.cpp | 13 +++++--- source/fuzz/fuzzer.h | 9 ++++- source/fuzz/fuzzer_pass.cpp | 27 ++++++++++++--- source/fuzz/fuzzer_pass.h | 7 +++- source/fuzz/fuzzer_pass_add_access_chains.cpp | 5 +-- source/fuzz/fuzzer_pass_add_access_chains.h | 3 +- .../fuzzer_pass_add_bit_instruction_synonyms.cpp | 5 +-- .../fuzzer_pass_add_bit_instruction_synonyms.h | 3 +- source/fuzz/fuzzer_pass_add_composite_extract.cpp | 5 +-- source/fuzz/fuzzer_pass_add_composite_extract.h | 3 +- source/fuzz/fuzzer_pass_add_composite_inserts.cpp | 5 +-- source/fuzz/fuzzer_pass_add_composite_inserts.h | 3 +- source/fuzz/fuzzer_pass_add_composite_types.cpp | 5 +-- source/fuzz/fuzzer_pass_add_composite_types.h | 3 +- source/fuzz/fuzzer_pass_add_copy_memory.cpp | 5 +-- source/fuzz/fuzzer_pass_add_copy_memory.h | 3 +- source/fuzz/fuzzer_pass_add_dead_blocks.cpp | 5 +-- source/fuzz/fuzzer_pass_add_dead_blocks.h | 3 +- source/fuzz/fuzzer_pass_add_dead_breaks.cpp | 5 +-- source/fuzz/fuzzer_pass_add_dead_breaks.h | 3 +- source/fuzz/fuzzer_pass_add_dead_continues.cpp | 5 +-- source/fuzz/fuzzer_pass_add_dead_continues.h | 9 ++--- .../fuzz/fuzzer_pass_add_equation_instructions.cpp | 5 +-- .../fuzz/fuzzer_pass_add_equation_instructions.h | 3 +- source/fuzz/fuzzer_pass_add_function_calls.cpp | 5 +-- source/fuzz/fuzzer_pass_add_function_calls.h | 9 ++--- source/fuzz/fuzzer_pass_add_global_variables.cpp | 5 +-- source/fuzz/fuzzer_pass_add_global_variables.h | 3 +- ...zer_pass_add_image_sample_unused_components.cpp | 5 +-- ...uzzer_pass_add_image_sample_unused_components.h | 3 +- source/fuzz/fuzzer_pass_add_loads.cpp | 5 +-- source/fuzz/fuzzer_pass_add_loads.h | 3 +- source/fuzz/fuzzer_pass_add_local_variables.cpp | 5 +-- source/fuzz/fuzzer_pass_add_local_variables.h | 3 +- source/fuzz/fuzzer_pass_add_loop_preheaders.cpp | 5 +-- source/fuzz/fuzzer_pass_add_loop_preheaders.h | 3 +- ...s_add_loops_to_create_int_constant_synonyms.cpp | 5 +-- ...ass_add_loops_to_create_int_constant_synonyms.h | 3 +- .../fuzzer_pass_add_no_contraction_decorations.cpp | 5 +-- .../fuzzer_pass_add_no_contraction_decorations.h | 3 +- source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp | 5 +-- source/fuzz/fuzzer_pass_add_opphi_synonyms.h | 9 ++--- source/fuzz/fuzzer_pass_add_parameters.cpp | 5 +-- source/fuzz/fuzzer_pass_add_parameters.h | 3 +- .../fuzz/fuzzer_pass_add_relaxed_decorations.cpp | 5 +-- source/fuzz/fuzzer_pass_add_relaxed_decorations.h | 3 +- source/fuzz/fuzzer_pass_add_stores.cpp | 5 +-- source/fuzz/fuzzer_pass_add_stores.h | 3 +- source/fuzz/fuzzer_pass_add_synonyms.cpp | 5 +-- source/fuzz/fuzzer_pass_add_synonyms.h | 3 +- ...fuzzer_pass_add_vector_shuffle_instructions.cpp | 5 +-- .../fuzzer_pass_add_vector_shuffle_instructions.h | 3 +- source/fuzz/fuzzer_pass_adjust_branch_weights.cpp | 5 +-- source/fuzz/fuzzer_pass_adjust_branch_weights.h | 3 +- .../fuzz/fuzzer_pass_adjust_function_controls.cpp | 5 +-- source/fuzz/fuzzer_pass_adjust_function_controls.h | 3 +- source/fuzz/fuzzer_pass_adjust_loop_controls.cpp | 5 +-- source/fuzz/fuzzer_pass_adjust_loop_controls.h | 3 +- .../fuzzer_pass_adjust_memory_operands_masks.cpp | 5 +-- .../fuzzer_pass_adjust_memory_operands_masks.h | 3 +- .../fuzz/fuzzer_pass_adjust_selection_controls.cpp | 5 +-- .../fuzz/fuzzer_pass_adjust_selection_controls.h | 3 +- source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 5 +-- source/fuzz/fuzzer_pass_apply_id_synonyms.h | 3 +- source/fuzz/fuzzer_pass_construct_composites.cpp | 5 +-- source/fuzz/fuzzer_pass_construct_composites.h | 3 +- source/fuzz/fuzzer_pass_copy_objects.cpp | 5 +-- source/fuzz/fuzzer_pass_copy_objects.h | 3 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 7 ++-- source/fuzz/fuzzer_pass_donate_modules.h | 3 +- ...zzer_pass_duplicate_regions_with_selections.cpp | 5 +-- ...fuzzer_pass_duplicate_regions_with_selections.h | 3 +- .../fuzz/fuzzer_pass_expand_vector_reductions.cpp | 5 +-- source/fuzz/fuzzer_pass_expand_vector_reductions.h | 3 +- .../fuzzer_pass_flatten_conditional_branches.cpp | 5 +-- .../fuzzer_pass_flatten_conditional_branches.h | 3 +- source/fuzz/fuzzer_pass_inline_functions.cpp | 5 +-- source/fuzz/fuzzer_pass_inline_functions.h | 3 +- ..._interchange_signedness_of_integer_operands.cpp | 5 +-- ...ss_interchange_signedness_of_integer_operands.h | 3 +- ...fuzzer_pass_interchange_zero_like_constants.cpp | 5 +-- .../fuzzer_pass_interchange_zero_like_constants.h | 3 +- .../fuzzer_pass_invert_comparison_operators.cpp | 5 +-- .../fuzz/fuzzer_pass_invert_comparison_operators.h | 3 +- .../fuzzer_pass_make_vector_operations_dynamic.cpp | 5 +-- .../fuzzer_pass_make_vector_operations_dynamic.h | 3 +- source/fuzz/fuzzer_pass_merge_blocks.cpp | 5 +-- source/fuzz/fuzzer_pass_merge_blocks.h | 3 +- source/fuzz/fuzzer_pass_merge_function_returns.cpp | 5 +-- source/fuzz/fuzzer_pass_merge_function_returns.h | 3 +- source/fuzz/fuzzer_pass_mutate_pointers.cpp | 5 +-- source/fuzz/fuzzer_pass_mutate_pointers.h | 3 +- source/fuzz/fuzzer_pass_obfuscate_constants.cpp | 5 +-- source/fuzz/fuzzer_pass_obfuscate_constants.h | 3 +- source/fuzz/fuzzer_pass_outline_functions.cpp | 5 +-- source/fuzz/fuzzer_pass_outline_functions.h | 9 ++--- source/fuzz/fuzzer_pass_permute_blocks.cpp | 5 +-- source/fuzz/fuzzer_pass_permute_blocks.h | 3 +- .../fuzzer_pass_permute_function_parameters.cpp | 5 +-- .../fuzz/fuzzer_pass_permute_function_parameters.h | 3 +- .../fuzzer_pass_permute_function_variables.cpp | 6 ++-- .../fuzz/fuzzer_pass_permute_function_variables.h | 3 +- source/fuzz/fuzzer_pass_permute_instructions.cpp | 5 +-- source/fuzz/fuzzer_pass_permute_instructions.h | 3 +- source/fuzz/fuzzer_pass_permute_phi_operands.cpp | 5 +-- source/fuzz/fuzzer_pass_permute_phi_operands.h | 3 +- .../fuzzer_pass_propagate_instructions_down.cpp | 5 +-- .../fuzz/fuzzer_pass_propagate_instructions_down.h | 3 +- .../fuzz/fuzzer_pass_propagate_instructions_up.cpp | 5 +-- .../fuzz/fuzzer_pass_propagate_instructions_up.h | 3 +- .../fuzzer_pass_push_ids_through_variables.cpp | 5 +-- .../fuzz/fuzzer_pass_push_ids_through_variables.h | 3 +- ...place_adds_subs_muls_with_carrying_extended.cpp | 5 +-- ...replace_adds_subs_muls_with_carrying_extended.h | 3 +- ...eplace_branches_from_dead_blocks_with_exits.cpp | 5 +-- ..._replace_branches_from_dead_blocks_with_exits.h | 3 +- ...ass_replace_copy_memories_with_loads_stores.cpp | 5 +-- ..._pass_replace_copy_memories_with_loads_stores.h | 3 +- ...pass_replace_copy_objects_with_stores_loads.cpp | 5 +-- ...r_pass_replace_copy_objects_with_stores_loads.h | 3 +- source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp | 5 +-- source/fuzz/fuzzer_pass_replace_irrelevant_ids.h | 3 +- ...er_pass_replace_linear_algebra_instructions.cpp | 5 +-- ...zzer_pass_replace_linear_algebra_instructions.h | 3 +- ...ass_replace_loads_stores_with_copy_memories.cpp | 5 +-- ..._pass_replace_loads_stores_with_copy_memories.h | 3 +- ...ss_replace_opphi_ids_from_dead_predecessors.cpp | 5 +-- ...pass_replace_opphi_ids_from_dead_predecessors.h | 3 +- ...replace_opselects_with_conditional_branches.cpp | 5 +-- ...s_replace_opselects_with_conditional_branches.h | 3 +- .../fuzzer_pass_replace_parameter_with_global.cpp | 5 +-- .../fuzzer_pass_replace_parameter_with_global.h | 3 +- .../fuzzer_pass_replace_params_with_struct.cpp | 5 +-- .../fuzz/fuzzer_pass_replace_params_with_struct.h | 3 +- source/fuzz/fuzzer_pass_split_blocks.cpp | 5 +-- source/fuzz/fuzzer_pass_split_blocks.h | 3 +- .../fuzz/fuzzer_pass_swap_commutable_operands.cpp | 5 +-- source/fuzz/fuzzer_pass_swap_commutable_operands.h | 3 +- ...uzzer_pass_swap_conditional_branch_operands.cpp | 5 +-- .../fuzzer_pass_swap_conditional_branch_operands.h | 3 +- source/fuzz/fuzzer_pass_swap_functions.cpp | 5 +-- source/fuzz/fuzzer_pass_swap_functions.h | 3 +- ...fuzzer_pass_toggle_access_chain_instruction.cpp | 5 +-- .../fuzzer_pass_toggle_access_chain_instruction.h | 3 +- .../fuzzer_pass_wrap_regions_in_selections.cpp | 5 +-- .../fuzz/fuzzer_pass_wrap_regions_in_selections.h | 3 +- source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp | 5 +-- source/fuzz/fuzzer_pass_wrap_vector_synonym.h | 3 +- test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp | 2 +- .../fuzz/fuzzer_pass_construct_composites_test.cpp | 4 +-- test/fuzz/fuzzer_pass_donate_modules_test.cpp | 38 +++++++++++----------- test/fuzz/fuzzer_pass_outline_functions_test.cpp | 8 ++--- test/fuzz/fuzzer_pass_test.cpp | 2 +- test/fuzz/fuzzer_replayer_test.cpp | 2 +- test/fuzz/fuzzer_shrinker_test.cpp | 2 +- test/fuzz/shrinker_test.cpp | 4 +-- tools/fuzz/fuzz.cpp | 2 +- 157 files changed, 451 insertions(+), 272 deletions(-) diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 61c4ddbe..7a52603f 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -110,7 +110,8 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, - spv_validator_options validator_options) + spv_validator_options validator_options, + bool ignore_inapplicable_transformations) : consumer_(std::move(consumer)), enable_all_passes_(enable_all_passes), validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass), @@ -124,7 +125,9 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, pass_instances_(), repeated_pass_recommender_(nullptr), repeated_pass_manager_(nullptr), - final_passes_() { + final_passes_(), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) { assert(ir_context_ && "IRContext is not initialized"); assert(fuzzer_context_ && "FuzzerContext is not initialized"); assert(transformation_context_ && "TransformationContext is not initialized"); @@ -257,7 +260,8 @@ void Fuzzer::MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, fuzzer_context_->ChoosePercentage(percentage_chance_of_adding_pass)) { pass_instances->SetPass(MakeUnique( ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), - &transformation_sequence_out_, std::forward(extra_args)...)); + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); } } @@ -267,7 +271,8 @@ void Fuzzer::MaybeAddFinalPass(std::vector>* passes, if (enable_all_passes_ || fuzzer_context_->ChooseEven()) { passes->push_back(MakeUnique( ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), - &transformation_sequence_out_, std::forward(extra_args)...)); + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); } } diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h index 1e7d6860..b969b3ed 100644 --- a/source/fuzz/fuzzer.h +++ b/source/fuzz/fuzzer.h @@ -62,7 +62,8 @@ class Fuzzer { const std::vector& donor_suppliers, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, - spv_validator_options validator_options); + spv_validator_options validator_options, + bool ignore_inapplicable_transformations); // Disables copy/move constructor/assignment operations. Fuzzer(const Fuzzer&) = delete; @@ -187,6 +188,12 @@ class Fuzzer { // Some passes that it does not make sense to apply repeatedly, as they do not // unlock other passes. std::vector> final_passes_; + + // When set, this flag causes inapplicable transformations that should be + // applicable by construction to be ignored. This is useful when the fuzzer + // is being deployed at scale to test a SPIR-V processing tool, and where it + // is desirable to ignore bugs in the fuzzer itself. + const bool ignore_inapplicable_transformations_; }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 9e95190c..d91482cc 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -43,11 +43,14 @@ namespace fuzz { FuzzerPass::FuzzerPass(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : ir_context_(ir_context), transformation_context_(transformation_context), fuzzer_context_(fuzzer_context), - transformations_(transformations) {} + transformations_(transformations), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) {} FuzzerPass::~FuzzerPass() = default; @@ -183,9 +186,23 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor( } void FuzzerPass::ApplyTransformation(const Transformation& transformation) { - assert(transformation.IsApplicable(GetIRContext(), - *GetTransformationContext()) && - "Transformation should be applicable by construction."); + if (ignore_inapplicable_transformations_) { + // If an applicable-by-construction transformation turns out to be + // inapplicable, this is a bug in the fuzzer. However, when deploying the + // fuzzer at scale for finding bugs in SPIR-V processing tools it is + // desirable to silently ignore such bugs. This code path caters for that + // scenario. + if (!transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + return; + } + } else { + // This code path caters for debugging bugs in the fuzzer, where an + // applicable-by-construction transformation turns out to be inapplicable. + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && + "Transformation should be applicable by construction."); + } transformation.Apply(GetIRContext(), GetTransformationContext()); auto transformation_message = transformation.ToMessage(); assert(transformation_message.transformation_case() != diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index ec254855..2655b540 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -33,7 +33,8 @@ class FuzzerPass { FuzzerPass(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); virtual ~FuzzerPass(); @@ -321,6 +322,10 @@ class FuzzerPass { TransformationContext* transformation_context_; FuzzerContext* fuzzer_context_; protobufs::TransformationSequence* transformations_; + // If set, then transformations that should be applicable by construction are + // still tested for applicability, and ignored if they turn out to be + // inapplicable. Otherwise, applicability by construction is asserted. + const bool ignore_inapplicable_transformations_; }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp index c498642c..39f193d9 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddAccessChains::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h index e80c2c6c..5e209cd2 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.h +++ b/source/fuzz/fuzzer_pass_add_access_chains.h @@ -28,7 +28,8 @@ class FuzzerPassAddAccessChains : public FuzzerPass { FuzzerPassAddAccessChains(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp index 78abf5be..1b0d7b10 100644 --- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddBitInstructionSynonyms::FuzzerPassAddBitInstructionSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddBitInstructionSynonyms::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h index 28f95779..38d81aa7 100644 --- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassAddBitInstructionSynonyms : public FuzzerPass { FuzzerPassAddBitInstructionSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp index 19f99024..dbbec0ca 100644 --- a/source/fuzz/fuzzer_pass_add_composite_extract.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeExtract::Apply() { std::vector composite_synonyms; diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.h b/source/fuzz/fuzzer_pass_add_composite_extract.h index 32ac190a..e7ed18af 100644 --- a/source/fuzz/fuzzer_pass_add_composite_extract.h +++ b/source/fuzz/fuzzer_pass_add_composite_extract.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeExtract : public FuzzerPass { FuzzerPassAddCompositeExtract( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp index cf314d30..2ac12de4 100644 --- a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeInserts::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.h b/source/fuzz/fuzzer_pass_add_composite_inserts.h index 4d511f6c..d9f42d58 100644 --- a/source/fuzz/fuzzer_pass_add_composite_inserts.h +++ b/source/fuzz/fuzzer_pass_add_composite_inserts.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeInserts : public FuzzerPass { FuzzerPassAddCompositeInserts( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp index 3dfbd690..af36ad06 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeTypes::Apply() { MaybeAddMissingVectorTypes(); diff --git a/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h index 89d48f8a..f16c79e3 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.h +++ b/source/fuzz/fuzzer_pass_add_composite_types.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeTypes : public FuzzerPass { FuzzerPassAddCompositeTypes( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/source/fuzz/fuzzer_pass_add_copy_memory.cpp index b654927f..6551f49f 100644 --- a/source/fuzz/fuzzer_pass_add_copy_memory.cpp +++ b/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCopyMemory::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.h b/source/fuzz/fuzzer_pass_add_copy_memory.h index 0f7db0c7..e258752c 100644 --- a/source/fuzz/fuzzer_pass_add_copy_memory.h +++ b/source/fuzz/fuzzer_pass_add_copy_memory.h @@ -27,7 +27,8 @@ class FuzzerPassAddCopyMemory : public FuzzerPass { FuzzerPassAddCopyMemory(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index 8c166a20..82d53ebb 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -31,9 +31,10 @@ const size_t kMaxTransformationsInOnePass = 100U; FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadBlocks::Apply() { // We iterate over all blocks in the module collecting up those at which we diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h index a87c05c3..4567e87d 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.h +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassAddDeadBlocks : public FuzzerPass { FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index 0c18da90..c3664c87 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadBreaks::Apply() { // We first collect up lots of possibly-applicable transformations. diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h index d1086fc4..361c3464 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.h +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.h @@ -26,7 +26,8 @@ class FuzzerPassAddDeadBreaks : public FuzzerPass { FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp index 1ab40b73..38814811 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadContinues::Apply() { // Consider every block in every function. diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h index bf0009e4..4f1bd601 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.h +++ b/source/fuzz/fuzzer_pass_add_dead_continues.h @@ -23,10 +23,11 @@ namespace fuzz { // A fuzzer pass for adding dead continue edges to the module. class FuzzerPassAddDeadContinues : public FuzzerPass { public: - FuzzerPassAddDeadContinues( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddDeadContinues(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 15554b7a..4bbded8e 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -45,9 +45,10 @@ bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) { FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddEquationInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h index dbec5bac..dc9a27b3 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.h +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -29,7 +29,8 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass { FuzzerPassAddEquationInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp index 2240696b..033f4a27 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddFunctionCalls::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h index 081510c1..80b03d7b 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.h +++ b/source/fuzz/fuzzer_pass_add_function_calls.h @@ -24,10 +24,11 @@ namespace fuzz { // anywhere, and (b) any functions, from dead blocks. class FuzzerPassAddFunctionCalls : public FuzzerPass { public: - FuzzerPassAddFunctionCalls( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddFunctionCalls(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index be78ee52..061f44d0 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddGlobalVariables::Apply() { SpvStorageClass variable_storage_class = SpvStorageClassPrivate; diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h index 3745c5c3..1496646e 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.h +++ b/source/fuzz/fuzzer_pass_add_global_variables.h @@ -27,7 +27,8 @@ class FuzzerPassAddGlobalVariables : public FuzzerPass { FuzzerPassAddGlobalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp index c10db720..19661d10 100644 --- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp +++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp @@ -27,9 +27,10 @@ FuzzerPassAddImageSampleUnusedComponents:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddImageSampleUnusedComponents::Apply() { // SPIR-V module to help understand the transformation. diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h index f5dea8bf..1a278931 100644 --- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h +++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h @@ -28,7 +28,8 @@ class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass { FuzzerPassAddImageSampleUnusedComponents( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index 2e50da26..ae6d9fdf 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddLoads::FuzzerPassAddLoads( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoads::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h index 3913c624..14460e7f 100644 --- a/source/fuzz/fuzzer_pass_add_loads.h +++ b/source/fuzz/fuzzer_pass_add_loads.h @@ -26,7 +26,8 @@ class FuzzerPassAddLoads : public FuzzerPass { FuzzerPassAddLoads(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp index 03a936ba..a4e739f8 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLocalVariables::Apply() { auto basic_type_ids_and_pointers = diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h index d73dae29..c969ba06 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.h +++ b/source/fuzz/fuzzer_pass_add_local_variables.h @@ -27,7 +27,8 @@ class FuzzerPassAddLocalVariables : public FuzzerPass { FuzzerPassAddLocalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp index 1cfed866..e8c9e964 100644 --- a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp +++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoopPreheaders::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/source/fuzz/fuzzer_pass_add_loop_preheaders.h index 8ac2dac8..2a13dbac 100644 --- a/source/fuzz/fuzzer_pass_add_loop_preheaders.h +++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.h @@ -30,7 +30,8 @@ class FuzzerPassAddLoopPreheaders : public FuzzerPass { FuzzerPassAddLoopPreheaders( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp index 69e0697f..1a65ef61 100644 --- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp @@ -29,9 +29,10 @@ FuzzerPassAddLoopsToCreateIntConstantSynonyms:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() { std::vector constants; diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h index 2eacef5f..14e17542 100644 --- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass { FuzzerPassAddLoopsToCreateIntConstantSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp index d0753100..8ae6e90a 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddNoContractionDecorations::Apply() { // Consider every instruction in every block in every function. diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h index 74212d87..7aeb26de 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -26,7 +26,8 @@ class FuzzerPassAddNoContractionDecorations : public FuzzerPass { FuzzerPassAddNoContractionDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp index be6e7ea1..73b6b0ac 100644 --- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddOpPhiSynonyms::Apply() { // Get a list of synonymous ids with the same type that can be used in the diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h index 9077118e..c45c9a8a 100644 --- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h @@ -25,10 +25,11 @@ namespace fuzz { // synonymous with the others. class FuzzerPassAddOpPhiSynonyms : public FuzzerPass { public: - FuzzerPassAddOpPhiSynonyms( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddOpPhiSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_parameters.cpp b/source/fuzz/fuzzer_pass_add_parameters.cpp index 784653d8..1cb6a79d 100644 --- a/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddParameters::FuzzerPassAddParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddParameters::Apply() { // Compute type candidates for the new parameter. diff --git a/source/fuzz/fuzzer_pass_add_parameters.h b/source/fuzz/fuzzer_pass_add_parameters.h index 47dde390..c79f0e09 100644 --- a/source/fuzz/fuzzer_pass_add_parameters.h +++ b/source/fuzz/fuzzer_pass_add_parameters.h @@ -30,7 +30,8 @@ class FuzzerPassAddParameters : public FuzzerPass { FuzzerPassAddParameters(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp index 58c6d1b2..b8c2b559 100644 --- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddRelaxedDecorations::Apply() { // Consider every instruction in every block in every function. diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h index 723c4a0a..51eb5942 100644 --- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h +++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h @@ -26,7 +26,8 @@ class FuzzerPassAddRelaxedDecorations : public FuzzerPass { FuzzerPassAddRelaxedDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp index f89428d3..3b956e3d 100644 --- a/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddStores::FuzzerPassAddStores( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddStores::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h index 9519c385..b18dde1f 100644 --- a/source/fuzz/fuzzer_pass_add_stores.h +++ b/source/fuzz/fuzzer_pass_add_stores.h @@ -28,7 +28,8 @@ class FuzzerPassAddStores : public FuzzerPass { FuzzerPassAddStores(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_synonyms.cpp b/source/fuzz/fuzzer_pass_add_synonyms.cpp index fd866f98..1d188deb 100644 --- a/source/fuzz/fuzzer_pass_add_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddSynonyms::FuzzerPassAddSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddSynonyms::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_synonyms.h b/source/fuzz/fuzzer_pass_add_synonyms.h index ccf4a886..b0c174bc 100644 --- a/source/fuzz/fuzzer_pass_add_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_synonyms.h @@ -27,7 +27,8 @@ class FuzzerPassAddSynonyms : public FuzzerPass { FuzzerPassAddSynonyms(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp index 0275607f..a29d1d34 100644 --- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddVectorShuffleInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h index c5af374c..64d6608a 100644 --- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h +++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h @@ -26,7 +26,8 @@ class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass { FuzzerPassAddVectorShuffleInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp index 3c4f3803..94428f72 100644 --- a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp +++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustBranchWeights::Apply() { // For all OpBranchConditional instructions, diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/source/fuzz/fuzzer_pass_adjust_branch_weights.h index de2f33d3..ae1ea345 100644 --- a/source/fuzz/fuzzer_pass_adjust_branch_weights.h +++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.h @@ -28,7 +28,8 @@ class FuzzerPassAdjustBranchWeights : public FuzzerPass { FuzzerPassAdjustBranchWeights( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp index b38bd212..1c2bc8cf 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustFunctionControls::Apply() { // Consider every function in the module. diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h index 5ef32a18..7a8c4927 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustFunctionControls : public FuzzerPass { FuzzerPassAdjustFunctionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp index 0f7cf037..fe855cad 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustLoopControls::Apply() { // Consider every merge instruction in the module (via looking through all diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h index 4ca670b7..25e83047 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustLoopControls : public FuzzerPass { FuzzerPassAdjustLoopControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp index 778d43ff..d2ff40e6 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustMemoryOperandsMasks::Apply() { // Consider every block in every function. diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h index a068b8dd..3197f91b 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h @@ -27,7 +27,8 @@ class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass { FuzzerPassAdjustMemoryOperandsMasks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp index d9b4e293..7d8e6b57 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustSelectionControls::Apply() { // Consider every merge instruction in the module (via looking through all diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h index 6931f942..ac55de79 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustSelectionControls : public FuzzerPass { FuzzerPassAdjustSelectionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 71208313..8965c15e 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassApplyIdSynonyms::Apply() { // Compute a closure of data synonym facts, to enrich the pool of synonyms diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h index b402b509..3da9c5d6 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassApplyIdSynonyms : public FuzzerPass { FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index bc78baed..ff022fcc 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassConstructComposites::FuzzerPassConstructComposites( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassConstructComposites::Apply() { // Gather up the ids of all composite types, but skip block-/buffer diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h index 333ac931..81413980 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.h +++ b/source/fuzz/fuzzer_pass_construct_composites.h @@ -29,7 +29,8 @@ class FuzzerPassConstructComposites : public FuzzerPass { FuzzerPassConstructComposites( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp index 09b5368a..80cc2a57 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassCopyObjects::FuzzerPassCopyObjects( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassCopyObjects::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h index 461cd979..2fb5a53f 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.h +++ b/source/fuzz/fuzzer_pass_copy_objects.h @@ -26,7 +26,8 @@ class FuzzerPassCopyObjects : public FuzzerPass { FuzzerPassCopyObjects(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 2f2ed50a..5bdf6973 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -45,10 +45,11 @@ FuzzerPassDonateModules::FuzzerPassDonateModules( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, - const std::vector& donor_suppliers) + bool ignore_inapplicable_transformations, + std::vector donor_suppliers) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations), - donor_suppliers_(donor_suppliers) {} + transformations, ignore_inapplicable_transformations), + donor_suppliers_(std::move(donor_suppliers)) {} void FuzzerPassDonateModules::Apply() { // If there are no donor suppliers, this fuzzer pass is a no-op. diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index 1581a8a4..924dd358 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -31,7 +31,8 @@ class FuzzerPassDonateModules : public FuzzerPass { opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, - const std::vector& donor_suppliers); + bool ignore_inapplicable_transformations, + std::vector donor_suppliers); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp index e08d65b1..3a9a7e66 100644 --- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp +++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp @@ -25,9 +25,10 @@ FuzzerPassDuplicateRegionsWithSelections:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassDuplicateRegionsWithSelections::Apply() { // Iterate over all of the functions in the module. diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h index 7cb1197d..74343a66 100644 --- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h +++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h @@ -29,7 +29,8 @@ class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass { FuzzerPassDuplicateRegionsWithSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp index 2dcfff1a..5bf0461d 100644 --- a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp +++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassExpandVectorReductions::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/source/fuzz/fuzzer_pass_expand_vector_reductions.h index ed0225df..c0673e12 100644 --- a/source/fuzz/fuzzer_pass_expand_vector_reductions.h +++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.h @@ -28,7 +28,8 @@ class FuzzerPassExpandVectorReductions : public FuzzerPass { FuzzerPassExpandVectorReductions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp index 84da7a74..70fa6a12 100644 --- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp +++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassFlattenConditionalBranches::FuzzerPassFlattenConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassFlattenConditionalBranches::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h index e7d7dc36..abfbc3c1 100644 --- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h +++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h @@ -25,7 +25,8 @@ class FuzzerPassFlattenConditionalBranches : public FuzzerPass { FuzzerPassFlattenConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_inline_functions.cpp b/source/fuzz/fuzzer_pass_inline_functions.cpp index 405afd8b..4024096f 100644 --- a/source/fuzz/fuzzer_pass_inline_functions.cpp +++ b/source/fuzz/fuzzer_pass_inline_functions.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassInlineFunctions::FuzzerPassInlineFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInlineFunctions::Apply() { // |function_call_instructions| are the instructions that will be inlined. diff --git a/source/fuzz/fuzzer_pass_inline_functions.h b/source/fuzz/fuzzer_pass_inline_functions.h index a6ed8cad..c4e0b831 100644 --- a/source/fuzz/fuzzer_pass_inline_functions.h +++ b/source/fuzz/fuzzer_pass_inline_functions.h @@ -28,7 +28,8 @@ class FuzzerPassInlineFunctions : public FuzzerPass { FuzzerPassInlineFunctions(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp index 675cae9a..d8780ff7 100644 --- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp +++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp @@ -27,9 +27,10 @@ FuzzerPassInterchangeSignednessOfIntegerOperands:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() { assert(!GetFuzzerContext()->IsWgslCompatible() && diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h index 11b8fb6e..6a10e897 100644 --- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h +++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h @@ -32,7 +32,8 @@ class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass { FuzzerPassInterchangeSignednessOfIntegerOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp index 5d0a2223..2c16cd5b 100644 --- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp +++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( opt::Instruction* declaration) { diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h index 012f03d3..bb4a4dc7 100644 --- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h +++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -33,7 +33,8 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass { FuzzerPassInterchangeZeroLikeConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp index 542748e9..130f7502 100644 --- a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp +++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInvertComparisonOperators::Apply() { GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) { diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/source/fuzz/fuzzer_pass_invert_comparison_operators.h index 5f015c28..d0d09edb 100644 --- a/source/fuzz/fuzzer_pass_invert_comparison_operators.h +++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.h @@ -27,7 +27,8 @@ class FuzzerPassInvertComparisonOperators : public FuzzerPass { FuzzerPassInvertComparisonOperators( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp index 7bf07a49..b755d235 100644 --- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp +++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMakeVectorOperationsDynamic::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h index da27825a..b6a46c78 100644 --- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h +++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h @@ -27,7 +27,8 @@ class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass { FuzzerPassMakeVectorOperationsDynamic( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp index a8ec784c..70200629 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.cpp +++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMergeBlocks::Apply() { // First we populate a sequence of transformations that we might consider diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h index 66cf4c64..46e257f1 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.h +++ b/source/fuzz/fuzzer_pass_merge_blocks.h @@ -26,7 +26,8 @@ class FuzzerPassMergeBlocks : public FuzzerPass { FuzzerPassMergeBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/source/fuzz/fuzzer_pass_merge_function_returns.cpp index ee82eca5..220f707b 100644 --- a/source/fuzz/fuzzer_pass_merge_function_returns.cpp +++ b/source/fuzz/fuzzer_pass_merge_function_returns.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassMergeFunctionReturns::FuzzerPassMergeFunctionReturns( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMergeFunctionReturns::Apply() { // The pass might add new functions to the module (due to wrapping early diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.h b/source/fuzz/fuzzer_pass_merge_function_returns.h index ddd2c9dc..a799b8be 100644 --- a/source/fuzz/fuzzer_pass_merge_function_returns.h +++ b/source/fuzz/fuzzer_pass_merge_function_returns.h @@ -31,7 +31,8 @@ class FuzzerPassMergeFunctionReturns : public FuzzerPass { FuzzerPassMergeFunctionReturns( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/source/fuzz/fuzzer_pass_mutate_pointers.cpp index f021a978..bbe05409 100644 --- a/source/fuzz/fuzzer_pass_mutate_pointers.cpp +++ b/source/fuzz/fuzzer_pass_mutate_pointers.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMutatePointers::FuzzerPassMutatePointers( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMutatePointers::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.h b/source/fuzz/fuzzer_pass_mutate_pointers.h index 5ef6a2ab..45c6a7cc 100644 --- a/source/fuzz/fuzzer_pass_mutate_pointers.h +++ b/source/fuzz/fuzzer_pass_mutate_pointers.h @@ -26,7 +26,8 @@ class FuzzerPassMutatePointers : public FuzzerPass { FuzzerPassMutatePointers(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index 32318e89..f60c1b4c 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -30,9 +30,10 @@ namespace fuzz { FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h index 82b1092c..30e64d28 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.h +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -30,7 +30,8 @@ class FuzzerPassObfuscateConstants : public FuzzerPass { FuzzerPassObfuscateConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp index bfde61f7..b90c12dc 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.cpp +++ b/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassOutlineFunctions::Apply() { std::vector original_functions; diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h index 45e52ff4..d80dc4a7 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.h +++ b/source/fuzz/fuzzer_pass_outline_functions.h @@ -24,10 +24,11 @@ namespace fuzz { // flow graph into their own functions. class FuzzerPassOutlineFunctions : public FuzzerPass { public: - FuzzerPassOutlineFunctions( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassOutlineFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp index 769c49f0..e55fae32 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.cpp +++ b/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteBlocks::Apply() { // For now we do something very simple: we randomly decide whether to move a diff --git a/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h index e40178e6..39326c2b 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.h +++ b/source/fuzz/fuzzer_pass_permute_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassPermuteBlocks : public FuzzerPass { FuzzerPassPermuteBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp index 9a61bea0..a8035b90 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteFunctionParameters::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h index a4bf2ca5..c5b6ad43 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.h +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -32,7 +32,8 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass { FuzzerPassPermuteFunctionParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/source/fuzz/fuzzer_pass_permute_function_variables.cpp index a4e19e3b..f8b9b450 100644 --- a/source/fuzz/fuzzer_pass_permute_function_variables.cpp +++ b/source/fuzz/fuzzer_pass_permute_function_variables.cpp @@ -29,9 +29,11 @@ namespace fuzz { FuzzerPassPermuteFunctionVariables::FuzzerPassPermuteFunctionVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} // Here we call parent constructor. + transformations, ignore_inapplicable_transformations) { +} // Here we call parent constructor. void FuzzerPassPermuteFunctionVariables::Apply() { // Permuting OpVariable instructions in each function. diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.h b/source/fuzz/fuzzer_pass_permute_function_variables.h index 47f1de28..2ebc15f2 100644 --- a/source/fuzz/fuzzer_pass_permute_function_variables.h +++ b/source/fuzz/fuzzer_pass_permute_function_variables.h @@ -26,7 +26,8 @@ class FuzzerPassPermuteFunctionVariables : public FuzzerPass { FuzzerPassPermuteFunctionVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_instructions.cpp b/source/fuzz/fuzzer_pass_permute_instructions.cpp index f17e0187..3ef76be4 100644 --- a/source/fuzz/fuzzer_pass_permute_instructions.cpp +++ b/source/fuzz/fuzzer_pass_permute_instructions.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteInstructions::Apply() { // We are iterating over all instructions in all basic blocks. diff --git a/source/fuzz/fuzzer_pass_permute_instructions.h b/source/fuzz/fuzzer_pass_permute_instructions.h index 027101dd..b7ccbccb 100644 --- a/source/fuzz/fuzzer_pass_permute_instructions.h +++ b/source/fuzz/fuzzer_pass_permute_instructions.h @@ -27,7 +27,8 @@ class FuzzerPassPermuteInstructions : public FuzzerPass { FuzzerPassPermuteInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp index f2cc5231..5fac9816 100644 --- a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp +++ b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermutePhiOperands::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.h b/source/fuzz/fuzzer_pass_permute_phi_operands.h index 79999562..30a9d4f5 100644 --- a/source/fuzz/fuzzer_pass_permute_phi_operands.h +++ b/source/fuzz/fuzzer_pass_permute_phi_operands.h @@ -27,7 +27,8 @@ class FuzzerPassPermutePhiOperands : public FuzzerPass { FuzzerPassPermutePhiOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp index 89e14370..4c46dcd3 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp +++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassPropagateInstructionsDown::FuzzerPassPropagateInstructionsDown( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPropagateInstructionsDown::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/source/fuzz/fuzzer_pass_propagate_instructions_down.h index a2a0aac8..18f01654 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_down.h +++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.h @@ -26,7 +26,8 @@ class FuzzerPassPropagateInstructionsDown : public FuzzerPass { FuzzerPassPropagateInstructionsDown( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp index 8cd7437b..5e45da83 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp +++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPropagateInstructionsUp::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/source/fuzz/fuzzer_pass_propagate_instructions_up.h index b89be48d..0cb8396b 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_up.h +++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.h @@ -27,7 +27,8 @@ class FuzzerPassPropagateInstructionsUp : public FuzzerPass { FuzzerPassPropagateInstructionsUp( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 40576564..a6c07b4b 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPushIdsThroughVariables::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/source/fuzz/fuzzer_pass_push_ids_through_variables.h index 53008ee2..71b69492 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.h +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.h @@ -28,7 +28,8 @@ class FuzzerPassPushIdsThroughVariables : public FuzzerPass { FuzzerPassPushIdsThroughVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp index 8a83d3bc..467e6134 100644 --- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp +++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp @@ -29,9 +29,10 @@ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() { std::vector instructions_for_transformation; diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h index 0e29a6c6..268655f9 100644 --- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h +++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h @@ -29,7 +29,8 @@ class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass { FuzzerPassReplaceAddsSubsMulsWithCarryingExtended( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp index a516f3d4..995657cc 100644 --- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp +++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp @@ -28,9 +28,10 @@ FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() { // OpKill can only be used as a terminator in a function that is guaranteed diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h index ab7e00e5..cdbb66f2 100644 --- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h +++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceBranchesFromDeadBlocksWithExits : public FuzzerPass { FuzzerPassReplaceBranchesFromDeadBlocksWithExits( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp index f17339a9..af1aacee 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp +++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceCopyMemoriesWithLoadsStores:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h index cffe1cb9..7d954abe 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h +++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass { FuzzerPassReplaceCopyMemoriesWithLoadsStores( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp index 24f2255b..d0992a3f 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp +++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceCopyObjectsWithStoresLoads:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h index e7b11ce1..2ffc00b6 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h +++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass { FuzzerPassReplaceCopyObjectsWithStoresLoads( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp index 7e9d7baa..4d55ae84 100644 --- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp +++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceIrrelevantIds::Apply() { // Keep track of the irrelevant ids. This includes all the ids that are diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h index 1dc6b5d3..80f8eb86 100644 --- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h +++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceIrrelevantIds : public FuzzerPass { FuzzerPassReplaceIrrelevantIds( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp index 0890c2fe..445dbfe1 100644 --- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp +++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceLinearAlgebraInstructions:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { // For each instruction, checks whether it is a linear algebra instruction. In diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h index 5d2f2042..5734bf1f 100644 --- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h +++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass { FuzzerPassReplaceLinearAlgebraInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp index f2cf80fa..38ac048b 100644 --- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp +++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp @@ -27,9 +27,10 @@ FuzzerPassReplaceLoadsStoresWithCopyMemories:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() { // We look for matching pairs of instructions OpLoad and diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h index f30fc2b7..f6209fc0 100644 --- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h +++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass { FuzzerPassReplaceLoadsStoresWithCopyMemories( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp index b0a3d57c..ea90a7ac 100644 --- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp +++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp @@ -24,9 +24,10 @@ FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() { // Keep a vector of the transformations to apply. diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h index a2bc1886..b01e242f 100644 --- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h +++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass { FuzzerPassReplaceOpPhiIdsFromDeadPredecessors( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp index 10bb90ad..72ed0936 100644 --- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp +++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp @@ -27,9 +27,10 @@ FuzzerPassReplaceOpSelectsWithConditionalBranches:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() { // Keep track of the instructions that we want to replace. We need to collect diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h index ec743890..174962ee 100644 --- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h +++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass { FuzzerPassReplaceOpSelectsWithConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp index 5c256bb0..7fb7b0d2 100644 --- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp +++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceParameterWithGlobal::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h index 2ae49469..4eb50866 100644 --- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h +++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass { FuzzerPassReplaceParameterWithGlobal( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp index c045e19f..f029316e 100644 --- a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp +++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceParamsWithStruct::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/source/fuzz/fuzzer_pass_replace_params_with_struct.h index f17f5207..3af7367a 100644 --- a/source/fuzz/fuzzer_pass_replace_params_with_struct.h +++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceParamsWithStruct : public FuzzerPass { FuzzerPassReplaceParamsWithStruct( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp index 7b493559..40a4151d 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.cpp +++ b/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassSplitBlocks::FuzzerPassSplitBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSplitBlocks::Apply() { // Gather up pointers to all the blocks in the module. We are then able to diff --git a/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h index 58f10ddb..b1b94240 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.h +++ b/source/fuzz/fuzzer_pass_split_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassSplitBlocks : public FuzzerPass { FuzzerPassSplitBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp index 27fadd17..dce65f08 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapCommutableOperands::Apply() { auto context = GetIRContext(); diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h index 93de1728..13d8fb6e 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.h +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -28,7 +28,8 @@ class FuzzerPassSwapCommutableOperands : public FuzzerPass { FuzzerPassSwapCommutableOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp index b145b3bc..f8bf111d 100644 --- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp +++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp @@ -27,9 +27,10 @@ FuzzerPassSwapBranchConditionalOperands:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapBranchConditionalOperands::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h index 0137f38b..7f71f9b1 100644 --- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h +++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h @@ -27,7 +27,8 @@ class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass { FuzzerPassSwapBranchConditionalOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_functions.cpp b/source/fuzz/fuzzer_pass_swap_functions.cpp index 171f6cb8..8eeec850 100644 --- a/source/fuzz/fuzzer_pass_swap_functions.cpp +++ b/source/fuzz/fuzzer_pass_swap_functions.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassSwapFunctions::FuzzerPassSwapFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapFunctions::Apply() { // Collect all function ids in a module. diff --git a/source/fuzz/fuzzer_pass_swap_functions.h b/source/fuzz/fuzzer_pass_swap_functions.h index ac551f69..7af527f0 100644 --- a/source/fuzz/fuzzer_pass_swap_functions.h +++ b/source/fuzz/fuzzer_pass_swap_functions.h @@ -26,7 +26,8 @@ class FuzzerPassSwapFunctions : public FuzzerPass { FuzzerPassSwapFunctions(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp index e5afd9ee..ac2b1565 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassToggleAccessChainInstruction::Apply() { auto context = GetIRContext(); diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h index ff2f5d45..f0b6166b 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -27,7 +27,8 @@ class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { FuzzerPassToggleAccessChainInstruction( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp index 66bbcd81..3a3b12c1 100644 --- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp +++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassWrapRegionsInSelections::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h index 822c308f..fc3d7df9 100644 --- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h +++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h @@ -27,7 +27,8 @@ class FuzzerPassWrapRegionsInSelections : public FuzzerPass { FuzzerPassWrapRegionsInSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp index ff917cd6..37c93b30 100644 --- a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassWrapVectorSynonym::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.h b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h index 647d99d2..51458383 100644 --- a/source/fuzz/fuzzer_pass_wrap_vector_synonym.h +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h @@ -26,7 +26,8 @@ class FuzzerPassWrapVectorSynonym : public FuzzerPass { FuzzerPassWrapVectorSynonym( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp index 734f47af..1045f8a1 100644 --- a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp +++ b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp @@ -134,7 +134,7 @@ TEST(FuzzerPassAddOpPhiSynonymsTest, HelperFunctions) { FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); SetUpIdSynonyms(transformation_context.GetFactManager()); diff --git a/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/test/fuzz/fuzzer_pass_construct_composites_test.cpp index a02176b2..a858e4ce 100644 --- a/test/fuzz/fuzzer_pass_construct_composites_test.cpp +++ b/test/fuzz/fuzzer_pass_construct_composites_test.cpp @@ -92,7 +92,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) { FuzzerPassConstructComposites fuzzer_pass( context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); fuzzer_pass.Apply(); @@ -173,7 +173,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) { FuzzerPassConstructComposites fuzzer_pass( context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); fuzzer_pass.Apply(); diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index a8e4acee..fe8e671d 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -210,7 +210,7 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -291,7 +291,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -422,7 +422,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -517,7 +517,7 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -589,7 +589,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -717,7 +717,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -813,7 +813,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -945,7 +945,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1081,7 +1081,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1163,7 +1163,7 @@ TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1250,7 +1250,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1354,7 +1354,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1426,7 +1426,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1536,7 +1536,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1720,7 +1720,7 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1792,7 +1792,7 @@ TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1949,7 +1949,7 @@ TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -2022,7 +2022,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesCapabilities) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability( SpvCapabilityVariablePointersStorageBuffer)); @@ -2255,7 +2255,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesOpPhisInMergeBlock) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); diff --git a/test/fuzz/fuzzer_pass_outline_functions_test.cpp b/test/fuzz/fuzzer_pass_outline_functions_test.cpp index 0d2c5bf7..a088e17b 100644 --- a/test/fuzz/fuzzer_pass_outline_functions_test.cpp +++ b/test/fuzz/fuzzer_pass_outline_functions_test.cpp @@ -130,7 +130,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 28 auto suitable_entry_block = @@ -173,7 +173,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 20 auto suitable_entry_block = @@ -297,7 +297,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 21 auto suitable_entry_block = @@ -464,7 +464,7 @@ TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 39 is not a merge block, so it is already suitable. auto suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining( diff --git a/test/fuzz/fuzzer_pass_test.cpp b/test/fuzz/fuzzer_pass_test.cpp index b035de74..31b8582a 100644 --- a/test/fuzz/fuzzer_pass_test.cpp +++ b/test/fuzz/fuzzer_pass_test.cpp @@ -29,7 +29,7 @@ class FuzzerPassMock : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, false) {} ~FuzzerPassMock() override = default; diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index 6dc7ffb1..a80bd379 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -1668,7 +1668,7 @@ void RunFuzzerAndReplayer(const std::string& shader, Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), kConsoleMessageConsumer, donor_suppliers, enable_all_passes, - strategies[strategy_index], true, validator_options); + strategies[strategy_index], true, validator_options, false); auto fuzzer_result = fuzzer.Run(0); // Cycle the repeated pass strategy so that we try a different one next time diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp index e7921169..acee03c7 100644 --- a/test/fuzz/fuzzer_shrinker_test.cpp +++ b/test/fuzz/fuzzer_shrinker_test.cpp @@ -1068,7 +1068,7 @@ void RunFuzzerAndShrinker(const std::string& shader, Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), kConsoleMessageConsumer, donor_suppliers, enable_all_passes, repeated_pass_strategy, - true, validator_options); + true, validator_options, false); auto fuzzer_result = fuzzer.Run(0); ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule, fuzzer_result.status); diff --git a/test/fuzz/shrinker_test.cpp b/test/fuzz/shrinker_test.cpp index 447ebecd..942de29a 100644 --- a/test/fuzz/shrinker_test.cpp +++ b/test/fuzz/shrinker_test.cpp @@ -171,7 +171,7 @@ TEST(ShrinkerTest, ReduceAddedFunctions) { protobufs::TransformationSequence transformations; FuzzerPassDonateModules pass(variant_ir_context.get(), &transformation_context, &fuzzer_context, - &transformations, {}); + &transformations, false, {}); pass.DonateSingleModule(donor_ir_context.get(), true); protobufs::FactSequence no_facts; @@ -349,7 +349,7 @@ TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) { protobufs::TransformationSequence transformations; FuzzerPassDonateModules pass(variant_ir_context.get(), &transformation_context, &fuzzer_context, - &transformations, {}); + &transformations, false, {}); pass.DonateSingleModule(donor_ir_context.get(), true); protobufs::FactSequence no_facts; diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index de72b2cd..306f9255 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp @@ -630,7 +630,7 @@ bool Fuzz(const spv_target_env& target_env, std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), message_consumer, donor_suppliers, fuzzer_options->all_passes_enabled, repeated_pass_strategy, - fuzzer_options->fuzzer_pass_validation_enabled, validator_options); + fuzzer_options->fuzzer_pass_validation_enabled, validator_options, false); auto fuzz_result = fuzzer.Run(0); if (fuzz_result.status == spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule) { -- cgit v1.2.3 From 983ee2313c6818d72dc0692b6a7824ca080357fc Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Wed, 28 Jul 2021 19:35:32 -0600 Subject: spirv-opt: Add specific handling of vulkan debug info differences (#4398) Co-authored-by: baldurk --- source/operand.cpp | 6 ++++++ source/opt/inline_pass.cpp | 8 ++++++++ source/opt/ir_loader.cpp | 30 ++++++++++++++++++++++++++++++ source/opt/module.cpp | 28 +++++++++++++++++++++++----- 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/source/operand.cpp b/source/operand.cpp index c00c9b64..bff36a26 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -578,6 +578,12 @@ std::function spvOperandCanBeForwardDeclaredFunction( std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( spv_ext_inst_type_t ext_type, uint32_t key) { + // The Vulkan debug info extended instruction set is non-semantic so allows no + // forward references ever. + if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + return [](unsigned) { return false; }; + } + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward // references for debug info instructions are still in discussion. We must // update the following lines of code when we conclude the spec. diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index ce3bf7f3..a6bdaaff 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp @@ -403,6 +403,14 @@ bool InlinePass::InlineEntryBlock( callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block); while (callee_inst_itr != callee_first_block->end()) { + // Don't inline function definition links, the calling function is not a + // definition. + if (callee_inst_itr->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + ++callee_inst_itr; + continue; + } + if (!InlineSingleInstruction( callee2caller, new_blk_ptr->get(), &*callee_inst_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index 77f90f19..bfdd59b3 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -17,6 +17,7 @@ #include #include "DebugInfo.h" +#include "NonSemanticVulkanDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/ext_inst.h" #include "source/opt/log.h" @@ -232,6 +233,35 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { block_->AddInstruction(std::move(spv_inst)); break; } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and " + "DebugValue found inside function", + opcode); + return false; + } + } + } else if (inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + const NonSemanticVulkanDebugInfo100Instructions ext_inst_key = + NonSemanticVulkanDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case NonSemanticVulkanDebugInfo100DebugDeclare: + case NonSemanticVulkanDebugInfo100DebugValue: + case NonSemanticVulkanDebugInfo100DebugScope: + case NonSemanticVulkanDebugInfo100DebugNoScope: + case NonSemanticVulkanDebugInfo100DebugFunctionDefinition: { + if (block_ == nullptr) { // Inside function but outside blocks + Errorf(consumer_, src, loc, + "Debug info extension instruction found inside function " + "but outside block", + opcode); + } else { + block_->AddInstruction(std::move(spv_inst)); + } + break; + } default: { Errorf(consumer_, src, loc, "Debug info extension instruction other than DebugScope, " diff --git a/source/opt/module.cpp b/source/opt/module.cpp index 0c886010..6585012f 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -145,8 +145,10 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { DebugScope last_scope(kNoDebugScope, kNoInlinedAt); const Instruction* last_line_inst = nullptr; bool between_merge_and_branch = false; + bool between_label_and_phi_var = false; auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst, - &between_merge_and_branch, this](const Instruction* i) { + &between_merge_and_branch, &between_label_and_phi_var, + this](const Instruction* i) { // Skip emitting line instructions between merge and branch instructions. auto opcode = i->opcode(); if (between_merge_and_branch && @@ -175,13 +177,29 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { last_line_inst = nullptr; } } + + if (opcode == SpvOpLabel) { + between_label_and_phi_var = true; + } else if (opcode != SpvOpVariable && opcode != SpvOpPhi && + opcode != SpvOpLine && opcode != SpvOpNoLine) { + between_label_and_phi_var = false; + } + 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); + // Can only emit nonsemantic instructions after all phi instructions + // in a block so don't emit scope instructions before phi instructions + // for Vulkan.NonSemantic.DebugInfo.100. + if (!between_label_and_phi_var || + context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()) { + // 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; } -- cgit v1.2.3 From c6422cff3375cefef0af448947bbd4806043d7ca Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Thu, 29 Jul 2021 07:30:48 -0500 Subject: spirv-opt: Rename ControlDependenceAnalysis::DoesBlockExist to HasBlock (#4412) Suggested by Jakub as 'DoesBlockExist' was confusing. --- source/opt/control_dependence.h | 6 ++---- test/opt/control_dependence.cpp | 13 ++++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/source/opt/control_dependence.h b/source/opt/control_dependence.h index 920e4dcf..993f3793 100644 --- a/source/opt/control_dependence.h +++ b/source/opt/control_dependence.h @@ -156,13 +156,11 @@ class ControlDependenceAnalysis { // Returns true if the block |id| exists in the control dependence graph. // This can be false even if the block exists in the function when it is part // of an infinite loop, since it is not part of the post-dominator tree. - bool DoesBlockExist(uint32_t id) const { - return forward_nodes_.count(id) > 0; - } + bool HasBlock(uint32_t id) const { return forward_nodes_.count(id) > 0; } // Returns true if block |a| is dependent on block |b|. bool IsDependent(uint32_t a, uint32_t b) const { - if (!DoesBlockExist(a)) return false; + if (!HasBlock(a)) return false; // BBs tend to have more dependents (targets) than they are dependent on // (sources), so search sources. const ControlDependenceList& a_sources = GetDependenceSources(a); diff --git a/test/opt/control_dependence.cpp b/test/opt/control_dependence.cpp index 94618d7e..46655472 100644 --- a/test/opt/control_dependence.cpp +++ b/test/opt/control_dependence.cpp @@ -122,16 +122,15 @@ TEST(ControlDependenceTest, DependenceSimpleCFG) { ControlDependenceAnalysis cdg; cdg.ComputeControlDependenceGraph(cfg, pdom); - // Test DoesBlockExist. + // Test HasBlock. for (uint32_t id = 10; id <= 19; id++) { - EXPECT_TRUE(cdg.DoesBlockExist(id)); + EXPECT_TRUE(cdg.HasBlock(id)); } - EXPECT_TRUE( - cdg.DoesBlockExist(ControlDependenceAnalysis::kPseudoEntryBlock)); + EXPECT_TRUE(cdg.HasBlock(ControlDependenceAnalysis::kPseudoEntryBlock)); // Check blocks before/after valid range. - EXPECT_FALSE(cdg.DoesBlockExist(5)); - EXPECT_FALSE(cdg.DoesBlockExist(25)); - EXPECT_FALSE(cdg.DoesBlockExist(UINT32_MAX)); + EXPECT_FALSE(cdg.HasBlock(5)); + EXPECT_FALSE(cdg.HasBlock(25)); + EXPECT_FALSE(cdg.HasBlock(UINT32_MAX)); // Test ForEachBlockLabel. std::set block_labels; -- cgit v1.2.3 From 17bf4437677cae8190e89ee52bc33b61686e8e7c Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 29 Jul 2021 17:09:14 +0100 Subject: spirv-fuzz: Add minimal SPIR-V example to test shaders (#4415) Testing on ClusterFuzz has revealed that the fuzzer sometimes goes wrong when a shader is very simple - e.g., there have been bugs where a fuzzer pass has assumed that at least one basic type exists in the module. This change adds an almost empty SPIR-V example to the shaders used for testing, to help catch such cases locally. --- test/fuzz/fuzzer_replayer_test.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index a80bd379..2a22e6a5 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -1596,6 +1596,23 @@ const std::string kTestShader6 = R"( OpFunctionEnd )"; +// A virtually empty piece of SPIR-V. + +const std::string kTestShader7 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + void AddConstantUniformFact(protobufs::FactSequence* facts, uint32_t descriptor_set, uint32_t binding, std::vector&& indices, uint32_t value) { @@ -1760,6 +1777,13 @@ TEST(FuzzerReplayerTest, Miscellaneous6) { kNumFuzzerRuns); } +TEST(FuzzerReplayerTest, Miscellaneous7) { + // Do some fuzzer runs, starting from an initial seed of 1 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader7, protobufs::FactSequence(), 1, + kNumFuzzerRuns); +} + } // namespace } // namespace fuzz } // namespace spvtools -- cgit v1.2.3 From 5737dbb068da91274de9728aab8b4bf27c52f38d Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 29 Jul 2021 12:56:58 -0400 Subject: Fix validator crash (#4418) Fixes #4411 * Some GLSL.std.450 validation didn't handle an operand without a type --- source/val/validate_extensions.cpp | 2 +- test/val/val_ext_inst_test.cpp | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index b5b98783..1f7b9563 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -812,7 +812,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { for (uint32_t operand_index = 4; operand_index < num_operands; ++operand_index) { const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (!_.IsIntScalarOrVectorType(operand_type)) { + if (!operand_type || !_.IsIntScalarOrVectorType(operand_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " << "expected all operands to be int scalars or vectors"; diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index b73ec341..06ecba63 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -3507,6 +3507,19 @@ TEST_P(ValidateGlslStd450SAbsLike, WrongBitWidthOperand) { "Result Type")); } +TEST_P(ValidateGlslStd450SAbsLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %main_entry\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllSAbsLike, ValidateGlslStd450SAbsLike, ::testing::ValuesIn(std::vector{ "SAbs", @@ -3656,6 +3669,19 @@ TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand2) { "Result Type")); } +TEST_P(ValidateGlslStd450UMinLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %s64_0 %main_entry\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllUMinLike, ValidateGlslStd450UMinLike, ::testing::ValuesIn(std::vector{ "UMin", @@ -3819,6 +3845,19 @@ TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand3) { "Result Type")); } +TEST_P(ValidateGlslStd450UClampLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %main_entry %s64_0 %s64_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllUClampLike, ValidateGlslStd450UClampLike, ::testing::ValuesIn(std::vector{ "UClamp", -- cgit v1.2.3 From c5bda7ae5acf9669f83467fa989e98193e2077b4 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Mon, 2 Aug 2021 13:37:37 +0100 Subject: Fuzzer: Default the new constructor parameter (#4438) Introducing a new mandatory parameter makes it very difficult to roll Chromium to a new version of SPIRV-Tools, as this project is used by several third-party projects, and an atomic update of all projects is very hard to coordinate. --- source/fuzz/fuzzer.cpp | 2 +- source/fuzz/fuzzer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 7a52603f..9838e64f 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -111,7 +111,7 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, spv_validator_options validator_options, - bool ignore_inapplicable_transformations) + bool ignore_inapplicable_transformations /* = true */) : consumer_(std::move(consumer)), enable_all_passes_(enable_all_passes), validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass), diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h index b969b3ed..4c38977f 100644 --- a/source/fuzz/fuzzer.h +++ b/source/fuzz/fuzzer.h @@ -63,7 +63,7 @@ class Fuzzer { bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, spv_validator_options validator_options, - bool ignore_inapplicable_transformations); + bool ignore_inapplicable_transformations = true); // Disables copy/move constructor/assignment operations. Fuzzer(const Fuzzer&) = delete; -- cgit v1.2.3 From affe280c22efcc0a8c9a1d05c8d51d739b6931e5 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 3 Aug 2021 15:27:03 +0100 Subject: Add GraphicsFuzz shaders to fuzzer corpus (#4429) Adds a set of SPIR-V shaders to the corpus used for fuzzing. Fixes #4428. --- test/fuzzers/corpora/spv/graphicsfuzz_000.spv | Bin 0 -> 7968 bytes test/fuzzers/corpora/spv/graphicsfuzz_001.spv | Bin 0 -> 2452 bytes test/fuzzers/corpora/spv/graphicsfuzz_002.spv | Bin 0 -> 2060 bytes test/fuzzers/corpora/spv/graphicsfuzz_003.spv | Bin 0 -> 13336 bytes test/fuzzers/corpora/spv/graphicsfuzz_004.spv | Bin 0 -> 12368 bytes test/fuzzers/corpora/spv/graphicsfuzz_005.spv | Bin 0 -> 2416 bytes test/fuzzers/corpora/spv/graphicsfuzz_006.spv | Bin 0 -> 2728 bytes test/fuzzers/corpora/spv/graphicsfuzz_007.spv | Bin 0 -> 2668 bytes test/fuzzers/corpora/spv/graphicsfuzz_008.spv | Bin 0 -> 22972 bytes test/fuzzers/corpora/spv/graphicsfuzz_009.spv | Bin 0 -> 3728 bytes test/fuzzers/corpora/spv/graphicsfuzz_010.spv | Bin 0 -> 3280 bytes test/fuzzers/corpora/spv/graphicsfuzz_011.spv | Bin 0 -> 5440 bytes test/fuzzers/corpora/spv/graphicsfuzz_012.spv | Bin 0 -> 3692 bytes test/fuzzers/corpora/spv/graphicsfuzz_013.spv | Bin 0 -> 2860 bytes test/fuzzers/corpora/spv/graphicsfuzz_014.spv | Bin 0 -> 1772 bytes test/fuzzers/corpora/spv/graphicsfuzz_015.spv | Bin 0 -> 7488 bytes test/fuzzers/corpora/spv/graphicsfuzz_016.spv | Bin 0 -> 4044 bytes test/fuzzers/corpora/spv/graphicsfuzz_017.spv | Bin 0 -> 14360 bytes test/fuzzers/corpora/spv/graphicsfuzz_018.spv | Bin 0 -> 16524 bytes test/fuzzers/corpora/spv/graphicsfuzz_019.spv | Bin 0 -> 10552 bytes test/fuzzers/corpora/spv/graphicsfuzz_020.spv | Bin 0 -> 9800 bytes test/fuzzers/corpora/spv/graphicsfuzz_021.spv | Bin 0 -> 1936 bytes test/fuzzers/corpora/spv/graphicsfuzz_022.spv | Bin 0 -> 9436 bytes test/fuzzers/corpora/spv/graphicsfuzz_023.spv | Bin 0 -> 1940 bytes test/fuzzers/corpora/spv/graphicsfuzz_024.spv | Bin 0 -> 9256 bytes test/fuzzers/corpora/spv/graphicsfuzz_025.spv | Bin 0 -> 7960 bytes test/fuzzers/corpora/spv/graphicsfuzz_026.spv | Bin 0 -> 9680 bytes test/fuzzers/corpora/spv/graphicsfuzz_027.spv | Bin 0 -> 9432 bytes test/fuzzers/corpora/spv/graphicsfuzz_028.spv | Bin 0 -> 4092 bytes test/fuzzers/corpora/spv/graphicsfuzz_029.spv | Bin 0 -> 2404 bytes test/fuzzers/corpora/spv/graphicsfuzz_030.spv | Bin 0 -> 2568 bytes test/fuzzers/corpora/spv/graphicsfuzz_031.spv | Bin 0 -> 6084 bytes test/fuzzers/corpora/spv/graphicsfuzz_032.spv | Bin 0 -> 11380 bytes test/fuzzers/corpora/spv/graphicsfuzz_033.spv | Bin 0 -> 7180 bytes test/fuzzers/corpora/spv/graphicsfuzz_034.spv | Bin 0 -> 1356 bytes test/fuzzers/corpora/spv/graphicsfuzz_035.spv | Bin 0 -> 648 bytes test/fuzzers/corpora/spv/graphicsfuzz_036.spv | Bin 0 -> 2872 bytes test/fuzzers/corpora/spv/graphicsfuzz_037.spv | Bin 0 -> 6256 bytes test/fuzzers/corpora/spv/graphicsfuzz_038.spv | Bin 0 -> 3648 bytes test/fuzzers/corpora/spv/graphicsfuzz_039.spv | Bin 0 -> 3428 bytes test/fuzzers/corpora/spv/graphicsfuzz_040.spv | Bin 0 -> 3284 bytes test/fuzzers/corpora/spv/graphicsfuzz_041.spv | Bin 0 -> 3360 bytes test/fuzzers/corpora/spv/graphicsfuzz_042.spv | Bin 0 -> 6456 bytes test/fuzzers/corpora/spv/graphicsfuzz_043.spv | Bin 0 -> 2084 bytes test/fuzzers/corpora/spv/graphicsfuzz_044.spv | Bin 0 -> 2596 bytes test/fuzzers/corpora/spv/graphicsfuzz_045.spv | Bin 0 -> 4680 bytes test/fuzzers/corpora/spv/graphicsfuzz_046.spv | Bin 0 -> 5816 bytes test/fuzzers/corpora/spv/graphicsfuzz_047.spv | Bin 0 -> 3216 bytes test/fuzzers/corpora/spv/graphicsfuzz_048.spv | Bin 0 -> 4644 bytes test/fuzzers/corpora/spv/graphicsfuzz_049.spv | Bin 0 -> 7436 bytes test/fuzzers/corpora/spv/graphicsfuzz_050.spv | Bin 0 -> 4156 bytes test/fuzzers/corpora/spv/graphicsfuzz_051.spv | Bin 0 -> 3200 bytes test/fuzzers/corpora/spv/graphicsfuzz_052.spv | Bin 0 -> 5572 bytes test/fuzzers/corpora/spv/graphicsfuzz_053.spv | Bin 0 -> 3936 bytes test/fuzzers/corpora/spv/graphicsfuzz_054.spv | Bin 0 -> 3024 bytes test/fuzzers/corpora/spv/graphicsfuzz_055.spv | Bin 0 -> 1220 bytes test/fuzzers/corpora/spv/graphicsfuzz_056.spv | Bin 0 -> 7660 bytes test/fuzzers/corpora/spv/graphicsfuzz_057.spv | Bin 0 -> 9044 bytes test/fuzzers/corpora/spv/graphicsfuzz_058.spv | Bin 0 -> 3172 bytes test/fuzzers/corpora/spv/graphicsfuzz_059.spv | Bin 0 -> 5940 bytes test/fuzzers/corpora/spv/graphicsfuzz_060.spv | Bin 0 -> 12388 bytes test/fuzzers/corpora/spv/graphicsfuzz_061.spv | Bin 0 -> 9564 bytes test/fuzzers/corpora/spv/graphicsfuzz_062.spv | Bin 0 -> 1824 bytes test/fuzzers/corpora/spv/graphicsfuzz_063.spv | Bin 0 -> 13152 bytes test/fuzzers/corpora/spv/graphicsfuzz_064.spv | Bin 0 -> 6284 bytes test/fuzzers/corpora/spv/graphicsfuzz_065.spv | Bin 0 -> 7184 bytes test/fuzzers/corpora/spv/graphicsfuzz_066.spv | Bin 0 -> 3796 bytes test/fuzzers/corpora/spv/graphicsfuzz_067.spv | Bin 0 -> 3716 bytes test/fuzzers/corpora/spv/graphicsfuzz_068.spv | Bin 0 -> 1956 bytes test/fuzzers/corpora/spv/graphicsfuzz_069.spv | Bin 0 -> 3740 bytes test/fuzzers/corpora/spv/graphicsfuzz_070.spv | Bin 0 -> 6024 bytes test/fuzzers/corpora/spv/graphicsfuzz_071.spv | Bin 0 -> 964 bytes test/fuzzers/corpora/spv/graphicsfuzz_072.spv | Bin 0 -> 20916 bytes test/fuzzers/corpora/spv/graphicsfuzz_073.spv | Bin 0 -> 10108 bytes test/fuzzers/corpora/spv/graphicsfuzz_074.spv | Bin 0 -> 5476 bytes test/fuzzers/corpora/spv/graphicsfuzz_075.spv | Bin 0 -> 1264 bytes test/fuzzers/corpora/spv/graphicsfuzz_076.spv | Bin 0 -> 3772 bytes test/fuzzers/corpora/spv/graphicsfuzz_077.spv | Bin 0 -> 4276 bytes test/fuzzers/corpora/spv/graphicsfuzz_078.spv | Bin 0 -> 7136 bytes test/fuzzers/corpora/spv/graphicsfuzz_079.spv | Bin 0 -> 5548 bytes test/fuzzers/corpora/spv/graphicsfuzz_080.spv | Bin 0 -> 5548 bytes test/fuzzers/corpora/spv/graphicsfuzz_081.spv | Bin 0 -> 3216 bytes test/fuzzers/corpora/spv/graphicsfuzz_082.spv | Bin 0 -> 16372 bytes test/fuzzers/corpora/spv/graphicsfuzz_083.spv | Bin 0 -> 4328 bytes test/fuzzers/corpora/spv/graphicsfuzz_084.spv | Bin 0 -> 4180 bytes test/fuzzers/corpora/spv/graphicsfuzz_085.spv | Bin 0 -> 2132 bytes test/fuzzers/corpora/spv/graphicsfuzz_086.spv | Bin 0 -> 2476 bytes 87 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_000.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_001.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_002.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_003.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_004.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_005.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_006.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_007.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_008.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_009.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_010.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_011.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_012.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_013.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_014.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_015.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_016.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_017.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_018.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_019.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_020.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_021.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_022.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_023.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_024.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_025.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_026.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_027.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_028.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_029.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_030.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_031.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_032.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_033.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_034.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_035.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_036.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_037.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_038.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_039.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_040.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_041.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_042.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_043.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_044.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_045.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_046.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_047.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_048.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_049.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_050.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_051.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_052.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_053.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_054.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_055.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_056.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_057.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_058.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_059.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_060.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_061.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_062.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_063.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_064.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_065.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_066.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_067.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_068.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_069.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_070.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_071.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_072.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_073.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_074.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_075.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_076.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_077.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_078.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_079.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_080.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_081.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_082.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_083.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_084.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_085.spv create mode 100644 test/fuzzers/corpora/spv/graphicsfuzz_086.spv diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_000.spv b/test/fuzzers/corpora/spv/graphicsfuzz_000.spv new file mode 100644 index 00000000..c203cec4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_000.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_001.spv b/test/fuzzers/corpora/spv/graphicsfuzz_001.spv new file mode 100644 index 00000000..c3dfbd9a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_001.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_002.spv b/test/fuzzers/corpora/spv/graphicsfuzz_002.spv new file mode 100644 index 00000000..5a3c145f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_002.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_003.spv b/test/fuzzers/corpora/spv/graphicsfuzz_003.spv new file mode 100644 index 00000000..c0e0a298 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_003.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_004.spv b/test/fuzzers/corpora/spv/graphicsfuzz_004.spv new file mode 100644 index 00000000..f1afc725 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_004.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_005.spv b/test/fuzzers/corpora/spv/graphicsfuzz_005.spv new file mode 100644 index 00000000..c6bf437d Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_005.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_006.spv b/test/fuzzers/corpora/spv/graphicsfuzz_006.spv new file mode 100644 index 00000000..d0d4a700 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_006.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_007.spv b/test/fuzzers/corpora/spv/graphicsfuzz_007.spv new file mode 100644 index 00000000..7f2f982d Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_007.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_008.spv b/test/fuzzers/corpora/spv/graphicsfuzz_008.spv new file mode 100644 index 00000000..c67dc976 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_008.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_009.spv b/test/fuzzers/corpora/spv/graphicsfuzz_009.spv new file mode 100644 index 00000000..68740b39 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_009.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_010.spv b/test/fuzzers/corpora/spv/graphicsfuzz_010.spv new file mode 100644 index 00000000..fedc8b4c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_010.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_011.spv b/test/fuzzers/corpora/spv/graphicsfuzz_011.spv new file mode 100644 index 00000000..882bfe6b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_011.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_012.spv b/test/fuzzers/corpora/spv/graphicsfuzz_012.spv new file mode 100644 index 00000000..bd3c1144 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_012.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_013.spv b/test/fuzzers/corpora/spv/graphicsfuzz_013.spv new file mode 100644 index 00000000..d7e35b4b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_013.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_014.spv b/test/fuzzers/corpora/spv/graphicsfuzz_014.spv new file mode 100644 index 00000000..abf54889 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_014.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_015.spv b/test/fuzzers/corpora/spv/graphicsfuzz_015.spv new file mode 100644 index 00000000..868ab046 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_015.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_016.spv b/test/fuzzers/corpora/spv/graphicsfuzz_016.spv new file mode 100644 index 00000000..a7cef28a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_016.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_017.spv b/test/fuzzers/corpora/spv/graphicsfuzz_017.spv new file mode 100644 index 00000000..6d338a91 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_017.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_018.spv b/test/fuzzers/corpora/spv/graphicsfuzz_018.spv new file mode 100644 index 00000000..c7bf440c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_018.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_019.spv b/test/fuzzers/corpora/spv/graphicsfuzz_019.spv new file mode 100644 index 00000000..99d7e2df Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_019.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_020.spv b/test/fuzzers/corpora/spv/graphicsfuzz_020.spv new file mode 100644 index 00000000..9e241246 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_020.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_021.spv b/test/fuzzers/corpora/spv/graphicsfuzz_021.spv new file mode 100644 index 00000000..02734af1 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_021.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_022.spv b/test/fuzzers/corpora/spv/graphicsfuzz_022.spv new file mode 100644 index 00000000..cd9ab76b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_022.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_023.spv b/test/fuzzers/corpora/spv/graphicsfuzz_023.spv new file mode 100644 index 00000000..d0ec5d31 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_023.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_024.spv b/test/fuzzers/corpora/spv/graphicsfuzz_024.spv new file mode 100644 index 00000000..503627f6 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_024.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_025.spv b/test/fuzzers/corpora/spv/graphicsfuzz_025.spv new file mode 100644 index 00000000..c4e0aa35 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_025.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_026.spv b/test/fuzzers/corpora/spv/graphicsfuzz_026.spv new file mode 100644 index 00000000..c5b3cedd Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_026.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_027.spv b/test/fuzzers/corpora/spv/graphicsfuzz_027.spv new file mode 100644 index 00000000..626e606f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_027.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_028.spv b/test/fuzzers/corpora/spv/graphicsfuzz_028.spv new file mode 100644 index 00000000..a9a43577 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_028.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_029.spv b/test/fuzzers/corpora/spv/graphicsfuzz_029.spv new file mode 100644 index 00000000..18e49a5a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_029.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_030.spv b/test/fuzzers/corpora/spv/graphicsfuzz_030.spv new file mode 100644 index 00000000..ac4ea364 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_030.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_031.spv b/test/fuzzers/corpora/spv/graphicsfuzz_031.spv new file mode 100644 index 00000000..2f8eff69 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_031.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_032.spv b/test/fuzzers/corpora/spv/graphicsfuzz_032.spv new file mode 100644 index 00000000..a5808ecf Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_032.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_033.spv b/test/fuzzers/corpora/spv/graphicsfuzz_033.spv new file mode 100644 index 00000000..98cf02e2 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_033.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_034.spv b/test/fuzzers/corpora/spv/graphicsfuzz_034.spv new file mode 100644 index 00000000..306fd845 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_034.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_035.spv b/test/fuzzers/corpora/spv/graphicsfuzz_035.spv new file mode 100644 index 00000000..7ed19e09 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_035.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_036.spv b/test/fuzzers/corpora/spv/graphicsfuzz_036.spv new file mode 100644 index 00000000..22e67839 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_036.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_037.spv b/test/fuzzers/corpora/spv/graphicsfuzz_037.spv new file mode 100644 index 00000000..19fbd20f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_037.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_038.spv b/test/fuzzers/corpora/spv/graphicsfuzz_038.spv new file mode 100644 index 00000000..7bc54890 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_038.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_039.spv b/test/fuzzers/corpora/spv/graphicsfuzz_039.spv new file mode 100644 index 00000000..01b908de Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_039.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_040.spv b/test/fuzzers/corpora/spv/graphicsfuzz_040.spv new file mode 100644 index 00000000..bd131571 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_040.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_041.spv b/test/fuzzers/corpora/spv/graphicsfuzz_041.spv new file mode 100644 index 00000000..6e355c20 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_041.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_042.spv b/test/fuzzers/corpora/spv/graphicsfuzz_042.spv new file mode 100644 index 00000000..d356cb19 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_042.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_043.spv b/test/fuzzers/corpora/spv/graphicsfuzz_043.spv new file mode 100644 index 00000000..49c87ed4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_043.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_044.spv b/test/fuzzers/corpora/spv/graphicsfuzz_044.spv new file mode 100644 index 00000000..c7e36d5c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_044.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_045.spv b/test/fuzzers/corpora/spv/graphicsfuzz_045.spv new file mode 100644 index 00000000..ad197263 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_045.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_046.spv b/test/fuzzers/corpora/spv/graphicsfuzz_046.spv new file mode 100644 index 00000000..3b3678ad Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_046.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_047.spv b/test/fuzzers/corpora/spv/graphicsfuzz_047.spv new file mode 100644 index 00000000..7253247b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_047.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_048.spv b/test/fuzzers/corpora/spv/graphicsfuzz_048.spv new file mode 100644 index 00000000..5c05c1c0 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_048.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_049.spv b/test/fuzzers/corpora/spv/graphicsfuzz_049.spv new file mode 100644 index 00000000..22bc9c21 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_049.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_050.spv b/test/fuzzers/corpora/spv/graphicsfuzz_050.spv new file mode 100644 index 00000000..8f847880 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_050.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_051.spv b/test/fuzzers/corpora/spv/graphicsfuzz_051.spv new file mode 100644 index 00000000..727c64d2 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_051.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_052.spv b/test/fuzzers/corpora/spv/graphicsfuzz_052.spv new file mode 100644 index 00000000..d9ba7418 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_052.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_053.spv b/test/fuzzers/corpora/spv/graphicsfuzz_053.spv new file mode 100644 index 00000000..d8270049 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_053.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_054.spv b/test/fuzzers/corpora/spv/graphicsfuzz_054.spv new file mode 100644 index 00000000..a3aec7b6 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_054.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_055.spv b/test/fuzzers/corpora/spv/graphicsfuzz_055.spv new file mode 100644 index 00000000..2da1375f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_055.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_056.spv b/test/fuzzers/corpora/spv/graphicsfuzz_056.spv new file mode 100644 index 00000000..515d4f1c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_056.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_057.spv b/test/fuzzers/corpora/spv/graphicsfuzz_057.spv new file mode 100644 index 00000000..2a0f4897 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_057.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_058.spv b/test/fuzzers/corpora/spv/graphicsfuzz_058.spv new file mode 100644 index 00000000..3211ec20 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_058.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_059.spv b/test/fuzzers/corpora/spv/graphicsfuzz_059.spv new file mode 100644 index 00000000..89e34ecf Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_059.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_060.spv b/test/fuzzers/corpora/spv/graphicsfuzz_060.spv new file mode 100644 index 00000000..d1fdaecc Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_060.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_061.spv b/test/fuzzers/corpora/spv/graphicsfuzz_061.spv new file mode 100644 index 00000000..de10f68b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_061.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_062.spv b/test/fuzzers/corpora/spv/graphicsfuzz_062.spv new file mode 100644 index 00000000..503304c3 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_062.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_063.spv b/test/fuzzers/corpora/spv/graphicsfuzz_063.spv new file mode 100644 index 00000000..75d9b4c4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_063.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_064.spv b/test/fuzzers/corpora/spv/graphicsfuzz_064.spv new file mode 100644 index 00000000..0b902cb2 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_064.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_065.spv b/test/fuzzers/corpora/spv/graphicsfuzz_065.spv new file mode 100644 index 00000000..437e45b0 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_065.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_066.spv b/test/fuzzers/corpora/spv/graphicsfuzz_066.spv new file mode 100644 index 00000000..5585a6f8 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_066.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_067.spv b/test/fuzzers/corpora/spv/graphicsfuzz_067.spv new file mode 100644 index 00000000..763fd34f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_067.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_068.spv b/test/fuzzers/corpora/spv/graphicsfuzz_068.spv new file mode 100644 index 00000000..1ad0bd50 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_068.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_069.spv b/test/fuzzers/corpora/spv/graphicsfuzz_069.spv new file mode 100644 index 00000000..f1e7950b Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_069.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_070.spv b/test/fuzzers/corpora/spv/graphicsfuzz_070.spv new file mode 100644 index 00000000..47f4c829 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_070.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_071.spv b/test/fuzzers/corpora/spv/graphicsfuzz_071.spv new file mode 100644 index 00000000..3eac74aa Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_071.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_072.spv b/test/fuzzers/corpora/spv/graphicsfuzz_072.spv new file mode 100644 index 00000000..e2461157 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_072.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_073.spv b/test/fuzzers/corpora/spv/graphicsfuzz_073.spv new file mode 100644 index 00000000..b7e915be Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_073.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_074.spv b/test/fuzzers/corpora/spv/graphicsfuzz_074.spv new file mode 100644 index 00000000..35887578 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_074.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_075.spv b/test/fuzzers/corpora/spv/graphicsfuzz_075.spv new file mode 100644 index 00000000..d3dc46dc Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_075.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_076.spv b/test/fuzzers/corpora/spv/graphicsfuzz_076.spv new file mode 100644 index 00000000..11bee684 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_076.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_077.spv b/test/fuzzers/corpora/spv/graphicsfuzz_077.spv new file mode 100644 index 00000000..d1b8c76a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_077.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_078.spv b/test/fuzzers/corpora/spv/graphicsfuzz_078.spv new file mode 100644 index 00000000..4739a9ac Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_078.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_079.spv b/test/fuzzers/corpora/spv/graphicsfuzz_079.spv new file mode 100644 index 00000000..6553ecf4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_079.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_080.spv b/test/fuzzers/corpora/spv/graphicsfuzz_080.spv new file mode 100644 index 00000000..802375b4 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_080.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_081.spv b/test/fuzzers/corpora/spv/graphicsfuzz_081.spv new file mode 100644 index 00000000..72d1027f Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_081.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_082.spv b/test/fuzzers/corpora/spv/graphicsfuzz_082.spv new file mode 100644 index 00000000..619c742c Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_082.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_083.spv b/test/fuzzers/corpora/spv/graphicsfuzz_083.spv new file mode 100644 index 00000000..fa27c33a Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_083.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_084.spv b/test/fuzzers/corpora/spv/graphicsfuzz_084.spv new file mode 100644 index 00000000..14f7bfdd Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_084.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_085.spv b/test/fuzzers/corpora/spv/graphicsfuzz_085.spv new file mode 100644 index 00000000..5f5fc03d Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_085.spv differ diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_086.spv b/test/fuzzers/corpora/spv/graphicsfuzz_086.spv new file mode 100644 index 00000000..a3119927 Binary files /dev/null and b/test/fuzzers/corpora/spv/graphicsfuzz_086.spv differ -- cgit v1.2.3 From 0065c5672d9e13d211806d441212e71f32a7ccb2 Mon Sep 17 00:00:00 2001 From: Mostafa Ashraf Date: Tue, 3 Aug 2021 22:51:25 +0200 Subject: spirv-fuzz: support AtomicLoad (#4330) Enhances the TransformationLoad transformation and associated fuzzer pass to support atomic operations. Fixes #4324. --- source/fuzz/fuzzer_context.cpp | 3 + source/fuzz/fuzzer_context.h | 4 + source/fuzz/fuzzer_pass_add_loads.cpp | 67 +++++- source/fuzz/protobufs/spvtoolsfuzz.proto | 19 +- source/fuzz/transformation_load.cpp | 177 +++++++++++++-- source/fuzz/transformation_load.h | 15 +- test/fuzz/transformation_load_test.cpp | 367 ++++++++++++++++++++++++++++--- 7 files changed, 582 insertions(+), 70 deletions(-) diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index eff702af..69ec68a0 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -42,6 +42,7 @@ const std::pair kChanceOfAddingAnotherPassToPassLoop = {50, const std::pair kChanceOfAddingAnotherStructField = {20, 90}; const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; +const std::pair KChanceOfAddingAtomicLoad = {30, 90}; const std::pair kChanceOfAddingBitInstructionSynonym = {5, 20}; const std::pair @@ -216,6 +217,8 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); chance_of_adding_array_or_struct_type_ = ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); + chance_of_adding_atomic_load_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicLoad); chance_of_adding_bit_instruction_synonym_ = ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym); chance_of_adding_both_branches_when_replacing_opselect_ = diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index b3f8cc35..e403aa04 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -142,6 +142,9 @@ class FuzzerContext { uint32_t GetChanceOfAddingArrayOrStructType() const { return chance_of_adding_array_or_struct_type_; } + uint32_t GetChanceOfAddingAtomicLoad() const { + return chance_of_adding_atomic_load_; + } uint32_t GetChanceOfAddingBitInstructionSynonym() const { return chance_of_adding_bit_instruction_synonym_; } @@ -492,6 +495,7 @@ class FuzzerContext { uint32_t chance_of_adding_another_pass_to_pass_loop_; uint32_t chance_of_adding_another_struct_field_; uint32_t chance_of_adding_array_or_struct_type_; + uint32_t chance_of_adding_atomic_load_; uint32_t chance_of_adding_bit_instruction_synonym_; uint32_t chance_of_adding_both_branches_when_replacing_opselect_; uint32_t chance_of_adding_composite_extract_; diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index ae6d9fdf..04dddbd6 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -39,18 +39,22 @@ void FuzzerPassAddLoads::Apply() { "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; } + // Check whether it is legitimate to insert a load or atomic load before + // this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { + return; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicLoad, + inst_it)) { + return; + } + std::vector relevant_instructions = FindAvailableInstructions( function, block, inst_it, @@ -80,13 +84,52 @@ void FuzzerPassAddLoads::Apply() { return; } - // Choose a pointer at random, and create and apply a loading - // transformation based on it. - ApplyTransformation(TransformationLoad( - GetFuzzerContext()->GetFreshId(), + auto chosen_instruction = relevant_instructions[GetFuzzerContext()->RandomIndex( - relevant_instructions)] - ->result_id(), + relevant_instructions)]; + + bool is_atomic_load = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = GetIRContext() + ->get_def_use_mgr() + ->GetDef(chosen_instruction->type_id()) + ->GetSingleWordInOperand(0); + + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassWorkgroup: + case SpvStorageClassCrossWorkgroup: + case SpvStorageClassAtomicCounter: + case SpvStorageClassImage: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicLoad())) { + is_atomic_load = true; + + memory_scope_id = FindOrCreateConstant( + {SpvScopeInvocation}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + TransformationLoad::GetMemorySemanticsForStorageClass( + static_cast(storage_class)))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. + ApplyTransformation(TransformationLoad( + GetFuzzerContext()->GetFreshId(), chosen_instruction->result_id(), + is_atomic_load, memory_scope_id, memory_semantics_id, instruction_descriptor)); }); } diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 5618c140..50fb1fd9 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -1569,17 +1569,26 @@ message TransformationInvertComparisonOperator { message TransformationLoad { - // Transformation that adds an OpLoad instruction from a pointer into an id. + // Transformation that adds an OpLoad or OpAtomicLoad instruction from a pointer into an id. - // The result of the load instruction + // The result of the load instruction. uint32 fresh_id = 1; - // The pointer to be loaded from + // The pointer to be loaded from. uint32 pointer_id = 2; + // True if and only if the load should be atomic. + bool is_atomic = 3; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 4; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 5; + // A descriptor for an instruction in a block before which the new OpLoad - // instruction should be inserted - InstructionDescriptor instruction_to_insert_before = 3; + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; } diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp index e22f8dd2..af90f1cb 100644 --- a/source/fuzz/transformation_load.cpp +++ b/source/fuzz/transformation_load.cpp @@ -24,10 +24,15 @@ TransformationLoad::TransformationLoad(protobufs::TransformationLoad message) : message_(std::move(message)) {} TransformationLoad::TransformationLoad( - uint32_t fresh_id, uint32_t pointer_id, + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, const protobufs::InstructionDescriptor& instruction_to_insert_before) { message_.set_fresh_id(fresh_id); message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); + *message_.mutable_instruction_to_insert_before() = instruction_to_insert_before; } @@ -68,11 +73,97 @@ bool TransformationLoad::IsApplicable( if (!insert_before) { return false; } - // ... and it must be legitimate to insert a store before it. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { + // ... and it must be legitimate to insert a load before it. + if (!message_.is_atomic() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { return false; } + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAtomicLoad, insert_before)) { + return false; + } + + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != SpvOpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != SpvOpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of SpvScopeInvocation. + auto memory_scope_const_value = + memory_scope_instruction->GetSingleWordInOperand(0); + if (memory_scope_const_value != SpvScopeInvocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + GetMemorySemanticsForStorageClass(static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + // The pointer needs to be available at the insertion point. return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.pointer_id()); @@ -80,22 +171,70 @@ bool TransformationLoad::IsApplicable( void TransformationLoad::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( - ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - auto new_instruction = MakeUnique( - ir_context, SpvOpLoad, result_type, message_.fresh_id(), - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); - auto new_instruction_ptr = new_instruction.get(); - insert_before->InsertBefore(std::move(new_instruction)); - // Inform the def-use manager about the new instruction and record its basic - // block. - ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); - ir_context->set_instr_block(new_instruction_ptr, - ir_context->get_instr_block(insert_before)); + if (message_.is_atomic()) { + // OpAtomicLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpAtomicLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } else { + // OpLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } +} + +SpvMemorySemanticsMask TransformationLoad::GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class) { + switch (storage_class) { + case SpvStorageClassWorkgroup: + return SpvMemorySemanticsWorkgroupMemoryMask; + + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + return SpvMemorySemanticsUniformMemoryMask; + + case SpvStorageClassCrossWorkgroup: + return SpvMemorySemanticsCrossWorkgroupMemoryMask; + + case SpvStorageClassAtomicCounter: + return SpvMemorySemanticsAtomicCounterMemoryMask; + + case SpvStorageClassImage: + return SpvMemorySemanticsImageMemoryMask; + + default: + return SpvMemorySemanticsMaskNone; + } } protobufs::Transformation TransformationLoad::ToMessage() const { diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h index d10b0073..92a20ab9 100644 --- a/source/fuzz/transformation_load.h +++ b/source/fuzz/transformation_load.h @@ -28,11 +28,20 @@ class TransformationLoad : public Transformation { explicit TransformationLoad(protobufs::TransformationLoad message); TransformationLoad( - uint32_t fresh_id, uint32_t pointer_id, + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, const protobufs::InstructionDescriptor& instruction_to_insert_before); // - |message_.fresh_id| must be fresh // - |message_.pointer_id| must be the id of a pointer + // - |message_.is_atomic| must be true if want to work with OpAtomicLoad + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // SpvScopeInvocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. // - 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 @@ -49,6 +58,10 @@ class TransformationLoad : public Transformation { void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; + // Returns memory semantics mask for specific storage class. + static SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class); + std::unordered_set GetFreshIds() const override; protobufs::Transformation ToMessage() const override; diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp index 3b6587f4..370826eb 100644 --- a/test/fuzz/transformation_load_test.cpp +++ b/test/fuzz/transformation_load_test.cpp @@ -129,61 +129,69 @@ TEST(TransformationLoadTest, BasicTest) { // 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(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(33, 33, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 11, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available - ASSERT_FALSE(TransformationLoad( - 100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 33, false, 0, 0, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable - ASSERT_FALSE(TransformationLoad( - 100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 27, false, 0, 0, + MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist ASSERT_FALSE( - TransformationLoad(100, 1000, + TransformationLoad(100, 1000, false, 0, 0, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) .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(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 5, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 24, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to load from null pointer - ASSERT_FALSE(TransformationLoad( - 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - - // Bad: %40 is not available at the program point ASSERT_FALSE( - TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + TransformationLoad(100, 60, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) .IsApplicable(context.get(), transformation_context)); - // Bad: The described instruction does not exist - ASSERT_FALSE(TransformationLoad( - 100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + // Bad: %40 is not available at the program point + ASSERT_FALSE(TransformationLoad(100, 40, false, 0, 0, + MakeInstructionDescriptor(37, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationLoad(100, 33, false, 0, 0, + MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + { TransformationLoad transformation( - 100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 100, 33, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -194,7 +202,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + 101, 46, false, 0, 0, + MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -205,7 +214,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + 102, 16, false, 0, 0, + MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -216,7 +226,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); + 103, 40, false, 0, 0, + MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -289,6 +300,296 @@ TEST(TransformationLoadTest, BasicTest) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationLoadTest, AtomicLoadTestCase) { + const std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Bad: id is not fresh. + ASSERT_FALSE( + TransformationLoad(14, 14, true, 15, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: id 100 of memory scope instruction does not exist. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 100, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: id 100 of memory semantics instruction does not exist. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 100, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory scope should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 5, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory semantics should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 5, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The memory scope instruction must have an Integer operand. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 19, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The memory memory semantics instruction must have an Integer operand. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 19, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of the memory scope must be equal to 32 bits. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 17, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of memory semantics must be equal to 32 bits. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 17, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory scope value must be 4 (SpvScopeInvocation). + ASSERT_FALSE( + TransformationLoad(21, 14, true, 16, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory semantics value must be either: + // 64 (SpvMemorySemanticsUniformMemoryMask) + // 256 (SpvMemorySemanticsWorkgroupMemoryMask) + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 16, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationLoad(21, 14, false, 15, 20, + MakeInstructionDescriptor(150, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationLoad transformation( + 21, 14, true, 15, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %15 %20 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationLoadTest, AtomicLoadTestCaseForWorkgroupMemory) { + std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %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 + %26 = OpTypeFloat 32 + %27 = OpTypeInt 8 1 + %7 = OpTypeInt 32 0 ; 0 means unsigned + %8 = OpConstant %7 0 + %17 = OpConstant %27 4 + %19 = OpConstant %26 0 + %9 = OpTypePointer Function %6 + %13 = OpTypeStruct %6 + %12 = OpTypePointer Workgroup %13 + %11 = OpVariable %12 Workgroup + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 4 + %23 = OpConstant %6 256 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Workgroup %6 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %50 %11 %14 + %40 = OpAccessChain %50 %11 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Bad: Can't insert OpAccessChain before the id 23 of memory scope. + ASSERT_FALSE( + TransformationLoad(60, 38, true, 21, 23, + MakeInstructionDescriptor(23, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 23 of memory semantics. + ASSERT_FALSE( + TransformationLoad(60, 38, true, 21, 23, + MakeInstructionDescriptor(21, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationLoad transformation( + 60, 38, true, 21, 23, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %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 + %26 = OpTypeFloat 32 + %27 = OpTypeInt 8 1 + %7 = OpTypeInt 32 0 ; 0 means unsigned + %8 = OpConstant %7 0 + %17 = OpConstant %27 4 + %19 = OpConstant %26 0 + %9 = OpTypePointer Function %6 + %13 = OpTypeStruct %6 + %12 = OpTypePointer Workgroup %13 + %11 = OpVariable %12 Workgroup + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 4 + %23 = OpConstant %6 256 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Workgroup %6 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %50 %11 %14 + %60 = OpAtomicLoad %6 %38 %21 %23 + %40 = OpAccessChain %50 %11 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools -- cgit v1.2.3 From 3ab6fb9c0328d30e4f2ee529a47f748b58755fde Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 5 Aug 2021 09:11:21 +0100 Subject: Add CMake rules for libFuzzer targets (#4445) Adds CMake rules for the libFuzzer targets, with a view to running them on OSS-Fuzz. Fixes #4448. --- CMakeLists.txt | 2 ++ kokoro/scripts/linux/build-docker.sh | 4 ++++ test/CMakeLists.txt | 1 + test/fuzzers/CMakeLists.txt | 44 ++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 test/fuzzers/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 84a7bb7e..12231724 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,8 @@ endif(SPIRV_BUILD_COMPRESSION) option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF) +option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF) + option(SPIRV_WERROR "Enable error on warning" ON) if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))) set(COMPILER_IS_LIKE_GNU TRUE) diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh index 553f0759..c6bbe951 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh @@ -79,6 +79,10 @@ if [ $TOOL = "cmake" ]; then SKIP_TESTS="True" fi + if [ $COMPILER = "clang" ]; then + ADDITIONAL_CMAKE_FLAGS="$ADDITIONAL_CMAKE_FLAGS -DSPIRV_BUILD_LIBFUZZER_TARGETS=ON" + fi + clean_dir "$ROOT_DIR/build" cd "$ROOT_DIR/build" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8ede58bf..88db2e18 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -192,3 +192,4 @@ add_subdirectory(fuzz) add_subdirectory(tools) add_subdirectory(util) add_subdirectory(val) +add_subdirectory(fuzzers) diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt new file mode 100644 index 00000000..6b0926ea --- /dev/null +++ b/test/fuzzers/CMakeLists.txt @@ -0,0 +1,44 @@ +# Copyright (c) 2021 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. + +function(add_spvtools_libfuzzer_target) + set(one_value_args TARGET) + set(multi_value_args SRCS LIBS) + cmake_parse_arguments( + ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN}) + + add_executable(${ARG_TARGET} ${ARG_SRCS}) + spvtools_default_compile_options(${ARG_TARGET}) + target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS}) + target_include_directories(${ARG_TARGET} PRIVATE + ${spirv-tools_SOURCE_DIR} + ${spirv-tools_BINARY_DIR} + ) + set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools libFuzzer targets") + target_compile_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + target_link_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") +endfunction() + +if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) + if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + message(FATAL_ERROR "The libFuzzer targets are only supported with the Clang compiler. Compiler '${CMAKE_CXX_COMPILER_ID}' is not supported!") + endif() + add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) +endif() -- cgit v1.2.3 From 366d1be5e831a9ae63525481536fb6044a488098 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 5 Aug 2021 12:00:33 +0100 Subject: fuzzers: Disable suggest-destructor-override warning (#4439) The protobuf generated code triggers this clang-12 warning, which some projects may treat as an error. --- source/fuzz/protobufs/spirvfuzz_protobufs.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h index eb8cb145..429f341e 100644 --- a/source/fuzz/protobufs/spirvfuzz_protobufs.h +++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -23,8 +23,10 @@ #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wunknown-warning-option" // Must come first #pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsuggest-destructor-override" +#pragma clang diagnostic ignored "-Wunused-parameter" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" -- cgit v1.2.3 From 07f1302352f72ff630db7fc593c5bbed974f99cb Mon Sep 17 00:00:00 2001 From: Mostafa Ashraf Date: Thu, 5 Aug 2021 15:08:44 +0200 Subject: spirv-fuzz: Support AtomicStore (#4440) Adds support for atomic operations in TransformationStore and its associated fuzzer pass. Fixes #4337. --- source/fuzz/fuzzer_context.cpp | 3 + source/fuzz/fuzzer_context.h | 4 + source/fuzz/fuzzer_pass_add_loads.cpp | 13 +- source/fuzz/fuzzer_pass_add_stores.cpp | 57 +++++- source/fuzz/fuzzer_util.cpp | 24 +++ source/fuzz/fuzzer_util.h | 4 + source/fuzz/protobufs/spvtoolsfuzz.proto | 21 ++- source/fuzz/transformation_load.cpp | 29 +-- source/fuzz/transformation_load.h | 4 - source/fuzz/transformation_store.cpp | 146 +++++++++++++-- source/fuzz/transformation_store.h | 11 +- test/fuzz/transformation_store_test.cpp | 303 +++++++++++++++++++++++++------ 12 files changed, 498 insertions(+), 121 deletions(-) diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 69ec68a0..c2175425 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -43,6 +43,7 @@ const std::pair kChanceOfAddingAnotherStructField = {20, 90}; const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; const std::pair KChanceOfAddingAtomicLoad = {30, 90}; +const std::pair KChanceOfAddingAtomicStore = {20, 90}; const std::pair kChanceOfAddingBitInstructionSynonym = {5, 20}; const std::pair @@ -219,6 +220,8 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); chance_of_adding_atomic_load_ = ChooseBetweenMinAndMax(KChanceOfAddingAtomicLoad); + chance_of_adding_atomic_store_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicStore); chance_of_adding_bit_instruction_synonym_ = ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym); chance_of_adding_both_branches_when_replacing_opselect_ = diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index e403aa04..77a5d400 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -145,6 +145,9 @@ class FuzzerContext { uint32_t GetChanceOfAddingAtomicLoad() const { return chance_of_adding_atomic_load_; } + uint32_t GetChanceOfAddingAtomicStore() const { + return chance_of_adding_atomic_store_; + } uint32_t GetChanceOfAddingBitInstructionSynonym() const { return chance_of_adding_bit_instruction_synonym_; } @@ -496,6 +499,7 @@ class FuzzerContext { uint32_t chance_of_adding_another_struct_field_; uint32_t chance_of_adding_array_or_struct_type_; uint32_t chance_of_adding_atomic_load_; + uint32_t chance_of_adding_atomic_store_; uint32_t chance_of_adding_bit_instruction_synonym_; uint32_t chance_of_adding_both_branches_when_replacing_opselect_; uint32_t chance_of_adding_composite_extract_; diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index 04dddbd6..ab91543b 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -92,10 +92,11 @@ void FuzzerPassAddLoads::Apply() { uint32_t memory_scope_id = 0; uint32_t memory_semantics_id = 0; - auto storage_class = GetIRContext() - ->get_def_use_mgr() - ->GetDef(chosen_instruction->type_id()) - ->GetSingleWordInOperand(0); + auto storage_class = static_cast( + GetIRContext() + ->get_def_use_mgr() + ->GetDef(chosen_instruction->type_id()) + ->GetSingleWordInOperand(0)); switch (storage_class) { case SpvStorageClassStorageBuffer: @@ -115,8 +116,8 @@ void FuzzerPassAddLoads::Apply() { memory_semantics_id = FindOrCreateConstant( {static_cast( - TransformationLoad::GetMemorySemanticsForStorageClass( - static_cast(storage_class)))}, + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), false); } diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp index 3b956e3d..606e4a63 100644 --- a/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -39,16 +39,20 @@ void FuzzerPassAddStores::Apply() { "The opcode of the instruction we might insert before must be " "the same as the opcode in the descriptor for the instruction"); + // Randomly decide whether to try inserting a store here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingStore())) { + return; + } + // 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())) { + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicStore, + inst_it)) { return; } @@ -117,10 +121,49 @@ void FuzzerPassAddStores::Apply() { return; } - // Choose a value at random, and create and apply a storing - // transformation based on it and the pointer. + bool is_atomic_store = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = + static_cast(GetIRContext() + ->get_def_use_mgr() + ->GetDef(pointer->type_id()) + ->GetSingleWordInOperand(0)); + + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassWorkgroup: + case SpvStorageClassCrossWorkgroup: + case SpvStorageClassAtomicCounter: + case SpvStorageClassImage: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicStore())) { + is_atomic_store = true; + + memory_scope_id = FindOrCreateConstant( + {SpvScopeInvocation}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. ApplyTransformation(TransformationStore( - pointer->result_id(), + pointer->result_id(), is_atomic_store, memory_scope_id, + memory_semantics_id, relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] ->result_id(), instruction_descriptor)); diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 3ab24ad6..ea7cde7f 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -455,6 +455,30 @@ uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, } } +SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class) { + switch (storage_class) { + case SpvStorageClassWorkgroup: + return SpvMemorySemanticsWorkgroupMemoryMask; + + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + return SpvMemorySemanticsUniformMemoryMask; + + case SpvStorageClassCrossWorkgroup: + return SpvMemorySemanticsCrossWorkgroupMemoryMask; + + case SpvStorageClassAtomicCounter: + return SpvMemorySemanticsAtomicCounterMemoryMask; + + case SpvStorageClassImage: + return SpvMemorySemanticsImageMemoryMask; + + default: + return SpvMemorySemanticsMaskNone; + } +} + bool IsValid(const opt::IRContext* context, spv_validator_options validator_options, MessageConsumer consumer) { diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index a6c92be0..e4697a1a 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -169,6 +169,10 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction, uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, opt::IRContext* ir_context); +// Returns memory semantics mask for specific storage class. +SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class); + // Returns true if and only if |context| is valid, according to the validator // instantiated with |validator_options|. |consumer| is used for error // reporting. diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 50fb1fd9..cba9397b 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -2245,17 +2245,26 @@ message TransformationSplitBlock { message TransformationStore { - // Transformation that adds an OpStore instruction of an id to a pointer. + // Transformation that adds an OpStore or OpAtomicStore instruction of an id to a pointer. - // The pointer to be stored to + // The pointer to be stored to. uint32 pointer_id = 1; - // The value to be stored - uint32 value_id = 2; + // True if and only if the load should be atomic. + bool is_atomic = 2; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 3; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 4; + + // The value to be stored. + uint32 value_id = 5; // A descriptor for an instruction in a block before which the new OpStore - // instruction should be inserted - InstructionDescriptor instruction_to_insert_before = 3; + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; } diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp index af90f1cb..bf48d996 100644 --- a/source/fuzz/transformation_load.cpp +++ b/source/fuzz/transformation_load.cpp @@ -158,8 +158,9 @@ bool TransformationLoad::IsApplicable( auto memory_semantics_const_value = static_cast( memory_semantics_instruction->GetSingleWordInOperand(0)); if (memory_semantics_const_value != - GetMemorySemanticsForStorageClass(static_cast( - pointer_type->GetSingleWordInOperand(0)))) { + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { return false; } } @@ -213,30 +214,6 @@ void TransformationLoad::Apply(opt::IRContext* ir_context, } } -SpvMemorySemanticsMask TransformationLoad::GetMemorySemanticsForStorageClass( - SpvStorageClass storage_class) { - switch (storage_class) { - case SpvStorageClassWorkgroup: - return SpvMemorySemanticsWorkgroupMemoryMask; - - case SpvStorageClassStorageBuffer: - case SpvStorageClassPhysicalStorageBuffer: - return SpvMemorySemanticsUniformMemoryMask; - - case SpvStorageClassCrossWorkgroup: - return SpvMemorySemanticsCrossWorkgroupMemoryMask; - - case SpvStorageClassAtomicCounter: - return SpvMemorySemanticsAtomicCounterMemoryMask; - - case SpvStorageClassImage: - return SpvMemorySemanticsImageMemoryMask; - - default: - return SpvMemorySemanticsMaskNone; - } -} - protobufs::Transformation TransformationLoad::ToMessage() const { protobufs::Transformation result; *result.mutable_load() = message_; diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h index 92a20ab9..57b4a535 100644 --- a/source/fuzz/transformation_load.h +++ b/source/fuzz/transformation_load.h @@ -58,10 +58,6 @@ class TransformationLoad : public Transformation { void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; - // Returns memory semantics mask for specific storage class. - static SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( - SpvStorageClass storage_class); - std::unordered_set GetFreshIds() const override; protobufs::Transformation ToMessage() const override; diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp index f8c6d013..c00cd345 100644 --- a/source/fuzz/transformation_store.cpp +++ b/source/fuzz/transformation_store.cpp @@ -24,9 +24,13 @@ TransformationStore::TransformationStore(protobufs::TransformationStore message) : message_(std::move(message)) {} TransformationStore::TransformationStore( - uint32_t pointer_id, uint32_t value_id, + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, uint32_t value_id, const protobufs::InstructionDescriptor& instruction_to_insert_before) { message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); message_.set_value_id(value_id); *message_.mutable_instruction_to_insert_before() = instruction_to_insert_before; @@ -70,8 +74,12 @@ bool TransformationStore::IsApplicable( return false; } // ... and it must be legitimate to insert a store before it. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, - insert_before)) { + if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpStore, insert_before)) { + return false; + } + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAtomicStore, insert_before)) { return false; } @@ -102,6 +110,87 @@ bool TransformationStore::IsApplicable( return false; } + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != SpvOpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != SpvOpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of SpvScopeInvocation. + auto memory_scope_const_value = + memory_scope_instruction->GetSingleWordInOperand(0); + if (memory_scope_const_value != SpvScopeInvocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + // The value needs to be available at the insertion point. return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.value_id()); @@ -109,20 +198,43 @@ bool TransformationStore::IsApplicable( void TransformationStore::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - auto new_instruction = MakeUnique( - ir_context, SpvOpStore, 0, 0, - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); - auto new_instruction_ptr = new_instruction.get(); - insert_before->InsertBefore(std::move(new_instruction)); - // Inform the def-use manager about the new instruction and record its basic - // block. - ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); - ir_context->set_instr_block(new_instruction_ptr, - ir_context->get_instr_block(insert_before)); + if (message_.is_atomic()) { + // OpAtomicStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpAtomicStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + + } else { + // OpStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } } protobufs::Transformation TransformationStore::ToMessage() const { diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h index 18ba1d7d..638713bb 100644 --- a/source/fuzz/transformation_store.h +++ b/source/fuzz/transformation_store.h @@ -28,12 +28,21 @@ class TransformationStore : public Transformation { explicit TransformationStore(protobufs::TransformationStore message); TransformationStore( - uint32_t pointer_id, uint32_t value_id, + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, 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_.is_atomic| must be true if want to work with OpAtomicStore. + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // SpvScopeInvocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. // - |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 diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp index ec24a73f..dd653e28 100644 --- a/test/fuzz/transformation_store_test.cpp +++ b/test/fuzz/transformation_store_test.cpp @@ -148,90 +148,107 @@ TEST(TransformationStoreTest, BasicTest) { // 61 - undefined // Bad: attempt to store to 11 from outside its function - ASSERT_FALSE(TransformationStore( - 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(11, false, 0, 0, 80, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available - ASSERT_FALSE(TransformationStore( - 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(81, false, 0, 0, 80, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable - ASSERT_FALSE(TransformationStore( - 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(52, false, 0, 0, 24, + MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist - ASSERT_FALSE(TransformationStore( - 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(1000, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(5, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(24, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(60, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(61, false, 0, 0, 21, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: %82 is not available at the program point ASSERT_FALSE( - TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + TransformationStore(82, false, 0, 0, 80, + MakeInstructionDescriptor(37, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: value id does not exist - ASSERT_FALSE(TransformationStore( - 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 1000, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 15, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 14, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(92, false, 0, 0, 93, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: value is not available - ASSERT_FALSE(TransformationStore( - 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 95, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .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(), transformation_context)); + ASSERT_FALSE( + TransformationStore(20, false, 0, 0, 95, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // The described instruction does not exist. - ASSERT_FALSE(TransformationStore( - 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 80, + MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); { // Store to irrelevant variable from dead block. TransformationStore transformation( - 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 27, false, 0, 0, 80, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -243,7 +260,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 11, false, 0, 0, 95, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -255,7 +273,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 46, false, 0, 0, 80, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -267,7 +286,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 16, false, 0, 0, 21, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -279,7 +299,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to non-irrelevant variable from dead block. TransformationStore transformation( - 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 53, false, 0, 0, 21, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -414,14 +435,188 @@ TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) { transformation_context.GetFactManager()->AddFactBlockIsDead(5); ASSERT_FALSE( - TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(15, false, 0, 0, 13, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( - TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(19, false, 0, 0, 50, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( - TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(27, false, 0, 0, 50, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationStoreTest, SupportAtomicStore) { + const std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %21 = OpConstant %6 15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 14); + + // Bad: id 100 of memory scope instruction does not exist. + ASSERT_FALSE( + TransformationStore(14, true, 100, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) .IsApplicable(context.get(), transformation_context)); + // Bad: id 100 of memory semantics instruction does not exist. + ASSERT_FALSE( + TransformationStore(14, true, 15, 100, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory scope should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationStore(14, true, 5, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory semantics should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationStore(14, true, 15, 5, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The memory scope instruction must have an Integer operand. + ASSERT_FALSE( + TransformationStore(14, true, 15, 19, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The memory memory semantics instruction must have an Integer operand. + ASSERT_FALSE( + TransformationStore(14, true, 19, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of the memory scope must be equal to 32 bits. + ASSERT_FALSE( + TransformationStore(14, true, 17, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of memory semantics must be equal to 32 bits. + ASSERT_FALSE( + TransformationStore(14, true, 15, 17, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory scope value must be 4 (SpvScopeInvocation). + ASSERT_FALSE( + TransformationStore(14, true, 16, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory semantics value must be either: + // 64 (SpvMemorySemanticsUniformMemoryMask) + // 256 (SpvMemorySemanticsWorkgroupMemoryMask) + ASSERT_FALSE( + TransformationStore(14, true, 15, 16, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(150, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 15 of memory scope. + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(15, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 20 of memory semantics. + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(20, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationStore transformation( + 14, true, 15, 20, 21, MakeInstructionDescriptor(24, SpvOpReturn, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %21 = OpConstant %6 15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpAtomicStore %14 %15 %20 %21 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } } // namespace -- cgit v1.2.3 From 175ecd49ed66cb3cb2593a4c6dd24468563bf17d Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 5 Aug 2021 09:56:52 -0400 Subject: Fix array layout validation slowdown (#4449) * Improve the early exit logic for array layout validation to handle more cases --- source/val/validate_decorations.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index f076b04c..615adc72 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -549,17 +549,23 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, // limitation to this check if the array size is a spec constant or is a // runtime array then we will only check a single element. This means // some improper straddles might be missed. - for (uint32_t i = 0; i < num_elements; ++i) { - uint32_t next_offset = i * array_stride + offset; - if (SpvOpTypeStruct == element_inst->opcode() && - SPV_SUCCESS != (recursive_status = checkLayout( - typeId, storage_class_str, decoration_str, - blockRules, scalar_block_layout, - next_offset, constraints, vstate))) - return recursive_status; - // If offsets accumulate up to a 16-byte multiple stop checking since - // it will just repeat. - if (i > 0 && (next_offset % 16 == 0)) break; + if (SpvOpTypeStruct == element_inst->opcode()) { + std::vector seen(16, false); + for (uint32_t i = 0; i < num_elements; ++i) { + uint32_t next_offset = i * array_stride + offset; + // Stop checking if offsets repeat in terms of 16-byte multiples. + if (seen[next_offset % 16]) { + break; + } + + if (SPV_SUCCESS != + (recursive_status = checkLayout( + typeId, storage_class_str, decoration_str, blockRules, + scalar_block_layout, next_offset, constraints, vstate))) + return recursive_status; + + seen[next_offset % 16] = true; + } } // Proceed to the element in case it is an array. -- cgit v1.2.3 From 3510a14cfc31e305d2ffa16548fb1292388bc2cd Mon Sep 17 00:00:00 2001 From: alan-baker Date: Fri, 6 Aug 2021 09:07:53 -0400 Subject: Add a section releases to the README (#4444) * Releases are unrelated to SPIRV-Headers releases * SPIRV-Headers should always be taken from the DEPS file --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 804b7637..9230fd1a 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,20 @@ following versions are ordered from oldest to newest: Use the `--version` option on each command line tool to see the software version. An API call reports the software version as a C-style string. +## Releases + +Some versions of SPIRV-Tools are tagged as stable releases (see +[tags](https://github.com/KhronosGroup/SPIRV-Tools/tags) on github). +These versions undergo extra testing. +Releases are not directly related to releases (or versions) of +[SPIRV-Headers][spirv-headers]. +Releases of SPIRV-Tools are tested against the version of SPIRV-Headers listed +in the DEPS file. +The release generally uses the most recent compatible version of SPIRV-Headers +available at the time of release. +No version of SPIRV-Headers other than the one listed in the DEPS file is +guaranteed to work with the SPIRV-Tools release. + ## Supported features ### Assembler, binary parser, and disassembler -- cgit v1.2.3 From 706dc27a62d8138cfef11aa3fe9eae9fa5344636 Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Fri, 6 Aug 2021 13:03:59 -0500 Subject: Add new target for spirv-lint (#4446) This PR adds a new executable spirv-lint with a simple "Hello, world!" program, along with its associated library and a dummy unit test. For now, only adds to CMake and Bazel; other build systems will be added in a future PR. Issue: #3196 --- BUILD.bazel | 35 +++++++++++++++++++++++++ build_defs.bzl | 17 +++++++++++++ include/spirv-tools/linter.hpp | 48 ++++++++++++++++++++++++++++++++++ source/CMakeLists.txt | 1 + source/lint/CMakeLists.txt | 58 ++++++++++++++++++++++++++++++++++++++++++ source/lint/linter.cpp | 51 +++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/lint/CMakeLists.txt | 18 +++++++++++++ test/lint/placeholder_test.cpp | 25 ++++++++++++++++++ tools/CMakeLists.txt | 3 ++- tools/lint/lint.cpp | 34 +++++++++++++++++++++++++ 11 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 include/spirv-tools/linter.hpp create mode 100644 source/lint/CMakeLists.txt create mode 100644 source/lint/linter.cpp create mode 100644 test/lint/CMakeLists.txt create mode 100644 test/lint/placeholder_test.cpp create mode 100644 tools/lint/lint.cpp diff --git a/BUILD.bazel b/BUILD.bazel index 4075441a..816f9328 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -14,6 +14,7 @@ load( "generate_vendor_tables", "generate_vendor_tables_local", "link_test", + "lint_test", "opt_test", "reduce_test", "util_test", @@ -233,6 +234,19 @@ cc_library( ], ) +cc_library( + name = "spirv_tools_lint", + srcs = glob(["source/lint/*.cpp"]), + hdrs = ["include/spirv-tools/linter.hpp"], + copts = COMMON_COPTS, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_opt", + ], +) + cc_library( name = "tools_util", srcs = glob(["tools/util/*.cpp"]), @@ -330,6 +344,20 @@ cc_binary( ], ) +cc_binary( + name = "spirv-lint", + srcs = [ + "tools/lint/lint.cpp", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_lint", + ":tools_util", + ], +) + cc_binary( name = "spirv-cfg", srcs = [ @@ -473,6 +501,13 @@ base_test( ["test/link/*.cpp"], )] +[lint_test( + name = f[10:-4], # strip test/lint/, .cpp + srcs = [f], +) for f in glob( + ["test/lint/*.cpp"], +)] + [opt_test( name = f[9:-4], # strip test/opt/, .cpp srcs = [f], diff --git a/build_defs.bzl b/build_defs.bzl index 638b16dd..519fa193 100644 --- a/build_defs.bzl +++ b/build_defs.bzl @@ -224,6 +224,23 @@ def base_test(name, srcs, deps = []): ] + deps, ) +def lint_test(name, srcs, deps = []): + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "lint_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":spirv_tools_lint", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + def link_test(name, srcs, deps = []): if name[-5:] != "_test": name = name + "_test" diff --git a/include/spirv-tools/linter.hpp b/include/spirv-tools/linter.hpp new file mode 100644 index 00000000..57d1b4e9 --- /dev/null +++ b/include/spirv-tools/linter.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2021 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 INCLUDE_SPIRV_TOOLS_LINTER_HPP_ +#define INCLUDE_SPIRV_TOOLS_LINTER_HPP_ + +#include "libspirv.hpp" + +namespace spvtools { + +// C++ interface for SPIR-V linting functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides a method for linting. +// +// Instances of this class provides basic thread-safety guarantee. +class Linter { + public: + explicit Linter(spv_target_env env); + + ~Linter(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns a reference to the registered message consumer. + const MessageConsumer& consumer() const; + + bool Run(const uint32_t* binary, size_t binary_size); + + private: + struct Impl; + std::unique_ptr impl_; +}; +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_LINTER_HPP_ diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e4568e35..6530f060 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -219,6 +219,7 @@ add_subdirectory(opt) add_subdirectory(reduce) add_subdirectory(fuzz) add_subdirectory(link) +add_subdirectory(lint) set(SPIRV_SOURCES ${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt new file mode 100644 index 00000000..b7630747 --- /dev/null +++ b/source/lint/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (c) 2021 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. +set(SPIRV_TOOLS_LINT_SOURCES + linter.cpp +) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib. + add_definitions(/MP4) +endif() + +spvtools_pch(SPIRV_TOOLS_LINT_SOURCES pch_source_lint) + +add_library(SPIRV-Tools-lint ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_LINT_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-lint) +target_include_directories(SPIRV-Tools-lint + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-lint + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) +# We need the internals of spirv-opt. +target_link_libraries(SPIRV-Tools-lint + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-lint PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-lint) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-lint EXPORT SPIRV-Tools-lintTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-lint PACKAGE_DIR) + install(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-lint) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-lintConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/source/lint/linter.cpp b/source/lint/linter.cpp new file mode 100644 index 00000000..0f847953 --- /dev/null +++ b/source/lint/linter.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2021 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 "spirv-tools/linter.hpp" + +namespace spvtools { + +struct Linter::Impl { + explicit Impl(spv_target_env env) : target_env(env) { + message_consumer = [](spv_message_level_t /*level*/, const char* /*source*/, + const spv_position_t& /*position*/, + const char* /*message*/) {}; + } + + spv_target_env target_env; // Target environment. + MessageConsumer message_consumer; // Message consumer. +}; + +Linter::Linter(spv_target_env env) : impl_(new Impl(env)) {} + +Linter::~Linter() {} + +void Linter::SetMessageConsumer(MessageConsumer consumer) { + impl_->message_consumer = consumer; +} + +const MessageConsumer& Linter::consumer() const { + return impl_->message_consumer; +} + +bool Linter::Run(const uint32_t* binary, size_t binary_size) { + (void)binary; + (void)binary_size; + + consumer()(SPV_MSG_INFO, "", {0, 0, 0}, "Hello, world!"); + + return true; +} + +} // namespace spvtools diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 88db2e18..e88df04d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -186,6 +186,7 @@ endif() add_subdirectory(link) +add_subdirectory(lint) add_subdirectory(opt) add_subdirectory(reduce) add_subdirectory(fuzz) diff --git a/test/lint/CMakeLists.txt b/test/lint/CMakeLists.txt new file mode 100644 index 00000000..b9ef208c --- /dev/null +++ b/test/lint/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2021 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. + +add_spvtools_unittest(TARGET lint + SRCS placeholder_test.cpp + LIBS SPIRV-Tools-lint SPIRV-Tools-opt +) diff --git a/test/lint/placeholder_test.cpp b/test/lint/placeholder_test.cpp new file mode 100644 index 00000000..a2ebec24 --- /dev/null +++ b/test/lint/placeholder_test.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2021 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 "gtest/gtest.h" + +namespace spvtools { +namespace lint { +namespace { + +TEST(PlaceholderTest, PlaceholderTest) { ASSERT_TRUE(true); } + +} // namespace +} // namespace lint +} // namespace spvtools diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 124a3328..6039089a 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -48,6 +48,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY}) endif() add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-lint SRCS lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-cfg SRCS cfg/cfg.cpp cfg/bin_to_dot.h @@ -56,7 +57,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR} ${SPIRV_HEADER_INCLUDE_DIR}) set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt - spirv-cfg spirv-link) + spirv-cfg spirv-link spirv-lint) if(NOT DEFINED IOS_PLATFORM) set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce) endif() diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp new file mode 100644 index 00000000..d4c8bde4 --- /dev/null +++ b/tools/lint/lint.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2021 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 + +#include "spirv-tools/linter.hpp" +#include "tools/util/cli_consumer.h" + +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +int main(int argc, const char** argv) { + (void)argc; + (void)argv; + + spv_target_env target_env = kDefaultEnvironment; + + spvtools::Linter linter(target_env); + linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + + bool ok = linter.Run(nullptr, 0); + + return ok ? 0 : 1; +} -- cgit v1.2.3 From f9d03bb40f553f7acf823591a1c82b25519caa76 Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Mon, 9 Aug 2021 14:30:51 -0500 Subject: Remove PCH from source/lint/CMakeLists.txt (#4459) Fixes #4458 --- source/lint/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt index b7630747..d996e3c9 100644 --- a/source/lint/CMakeLists.txt +++ b/source/lint/CMakeLists.txt @@ -20,8 +20,6 @@ if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) add_definitions(/MP4) endif() -spvtools_pch(SPIRV_TOOLS_LINT_SOURCES pch_source_lint) - add_library(SPIRV-Tools-lint ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_LINT_SOURCES}) spvtools_default_compile_options(SPIRV-Tools-lint) -- cgit v1.2.3 From c4c6f2ba5c3259ddbc4790119a919db2deda7275 Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Mon, 9 Aug 2021 15:43:36 -0500 Subject: spirv-opt: Add dataflow analysis framework (#4402) This PR adds a generic dataflow analysis framework to SPIRV-opt, with the intent of being used in SPIRV-lint. This may also be useful for SPIRV-opt, as existing ad-hoc analyses can be rewritten to use a common framework, but this is not the target of this PR. --- Android.mk | 1 + BUILD.gn | 2 + source/opt/CMakeLists.txt | 2 + source/opt/dataflow.cpp | 91 +++++++++++++++++++ source/opt/dataflow.h | 148 ++++++++++++++++++++++++++++++ test/opt/CMakeLists.txt | 1 + test/opt/dataflow.cpp | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 470 insertions(+) create mode 100644 source/opt/dataflow.cpp create mode 100644 source/opt/dataflow.h create mode 100644 test/opt/dataflow.cpp diff --git a/Android.mk b/Android.mk index 057731fc..ff856fe9 100644 --- a/Android.mk +++ b/Android.mk @@ -91,6 +91,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/control_dependence.cpp \ source/opt/convert_to_half_pass.cpp \ source/opt/copy_prop_arrays.cpp \ + source/opt/dataflow.cpp \ source/opt/dead_branch_elim_pass.cpp \ source/opt/dead_insert_elim_pass.cpp \ source/opt/dead_variable_elimination.cpp \ diff --git a/BUILD.gn b/BUILD.gn index fea02797..036a0a1c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -612,6 +612,8 @@ static_library("spvtools_opt") { "source/opt/convert_to_half_pass.h", "source/opt/copy_prop_arrays.cpp", "source/opt/copy_prop_arrays.h", + "source/opt/dataflow.h", + "source/opt/dataflow.cpp", "source/opt/dead_branch_elim_pass.cpp", "source/opt/dead_branch_elim_pass.h", "source/opt/dead_insert_elim_pass.cpp", diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index f6ebcfa6..b6027c08 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -30,6 +30,7 @@ set(SPIRV_TOOLS_OPT_SOURCES control_dependence.h convert_to_half_pass.h copy_prop_arrays.h + dataflow.h dead_branch_elim_pass.h dead_insert_elim_pass.h dead_variable_elimination.h @@ -137,6 +138,7 @@ set(SPIRV_TOOLS_OPT_SOURCES control_dependence.cpp convert_to_half_pass.cpp copy_prop_arrays.cpp + dataflow.cpp dead_branch_elim_pass.cpp dead_insert_elim_pass.cpp dead_variable_elimination.cpp diff --git a/source/opt/dataflow.cpp b/source/opt/dataflow.cpp new file mode 100644 index 00000000..c91fad08 --- /dev/null +++ b/source/opt/dataflow.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2021 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/opt/dataflow.h" + +#include +#include + +namespace spvtools { +namespace opt { + +bool DataFlowAnalysis::Enqueue(Instruction* inst) { + bool& is_enqueued = on_worklist_[inst]; + if (is_enqueued) return false; + is_enqueued = true; + worklist_.push(inst); + return true; +} + +DataFlowAnalysis::VisitResult DataFlowAnalysis::RunOnce( + Function* function, bool is_first_iteration) { + InitializeWorklist(function, is_first_iteration); + VisitResult ret = VisitResult::kResultFixed; + while (!worklist_.empty()) { + Instruction* top = worklist_.front(); + worklist_.pop(); + on_worklist_[top] = false; + VisitResult result = Visit(top); + if (result == VisitResult::kResultChanged) { + EnqueueSuccessors(top); + ret = VisitResult::kResultChanged; + } + } + return ret; +} + +void DataFlowAnalysis::Run(Function* function) { + VisitResult result = RunOnce(function, true); + while (result == VisitResult::kResultChanged) { + result = RunOnce(function, false); + } +} + +void ForwardDataFlowAnalysis::InitializeWorklist(Function* function, + bool /*is_first_iteration*/) { + context().cfg()->ForEachBlockInReversePostOrder( + function->entry().get(), [this](BasicBlock* bb) { + if (label_position_ == LabelPosition::kLabelsOnly) { + Enqueue(bb->GetLabelInst()); + return; + } + if (label_position_ == LabelPosition::kLabelsAtBeginning) { + Enqueue(bb->GetLabelInst()); + } + for (Instruction& inst : *bb) { + Enqueue(&inst); + } + if (label_position_ == LabelPosition::kLabelsAtEnd) { + Enqueue(bb->GetLabelInst()); + } + }); +} + +void ForwardDataFlowAnalysis::EnqueueUsers(Instruction* inst) { + context().get_def_use_mgr()->ForEachUser( + inst, [this](Instruction* user) { Enqueue(user); }); +} + +void ForwardDataFlowAnalysis::EnqueueBlockSuccessors(Instruction* inst) { + if (inst->opcode() != SpvOpLabel) return; + context() + .cfg() + ->block(inst->result_id()) + ->ForEachSuccessorLabel([this](uint32_t* label) { + Enqueue(context().cfg()->block(*label)->GetLabelInst()); + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/dataflow.h b/source/opt/dataflow.h new file mode 100644 index 00000000..be07415d --- /dev/null +++ b/source/opt/dataflow.h @@ -0,0 +1,148 @@ +// Copyright (c) 2021 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_OPT_DATAFLOW_H_ +#define SOURCE_OPT_DATAFLOW_H_ + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Generic data-flow analysis. +// Maintains a worklist of instructions to process and processes them in a +// specified order. See also ForwardDataFlowAnalysis, which is specialized for +// forward data-flow analysis. +class DataFlowAnalysis { + public: + // The result of a |Visit| operation on an instruction. + // This is used to determine when analysis has reached a fixpoint. + enum class VisitResult { + // The analysis result for this instruction has changed. + // This means that any instructions that depend on it (its successors) must + // be recomputed. + kResultChanged, + // The analysis result for this instruction has not changed. + // When all visit operations return |kResultFixed|, the analysis has reached + // a fixpoint (converged). + kResultFixed, + }; + + virtual ~DataFlowAnalysis() {} + + // Run this analysis on a given function. + // For analyses which work interprocedurally, |function| may be ignored. + void Run(Function* function); + + protected: + DataFlowAnalysis(IRContext& context) : context_(context) {} + + // Initialize the worklist for a given function. + // |is_first_iteration| is true on the first call to |Run| and false + // afterwards. All subsequent runs are only necessary to check if the analysis + // has converged; if |EnqueueSuccessors| is complete, |InitializeWorklist| + // should do nothing after the first iteration. + virtual void InitializeWorklist(Function* function, + bool is_first_iteration) = 0; + + // Enqueues the successors (instructions which use the analysis result) of + // |inst|. This is not required to be complete, but convergence is faster when + // it is. This is called whenever |Visit| returns |kResultChanged|. + virtual void EnqueueSuccessors(Instruction* inst) = 0; + + // Visits the given instruction, recomputing the analysis result. This is + // called once per instruction queued in |InitializeWorklist| and afterward + // when a predecessor is changed, through |EnqueueSuccessors|. + virtual VisitResult Visit(Instruction* inst) = 0; + + // Enqueues the given instruction to be visited. Ignored if already in the + // worklist. + bool Enqueue(Instruction* inst); + + IRContext& context() { return context_; } + + private: + // Runs one pass, calling |InitializeWorklist| and then iterating through the + // worklist until all fixed. + VisitResult RunOnce(Function* function, bool is_first_iteration); + + IRContext& context_; + std::unordered_map on_worklist_; + // The worklist, which contains the list of instructions to be visited. + // + // The choice of data structure was influenced by the data in "Iterative + // Data-flow Analysis, Revisited" (Cooper et al, 2002). + // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.125.1549&rep=rep1&type=pdf + // The paper shows that the overall performance benefit of a priority queue + // over a regular queue or stack is relatively small (or negative). + // + // A queue has the advantage that nodes are visited in the same order they are + // enqueued, which relieves the analysis from inserting nodes "backwards", for + // example in worklist initialization. Also, as the paper claims that sorting + // successors does not improve runtime, we can use a single queue which is + // modified during iteration. + std::queue worklist_; +}; + +// A generic data flow analysis, specialized for forward analysis. +class ForwardDataFlowAnalysis : public DataFlowAnalysis { + public: + // Indicates where labels should be in the worklist RPO ordering. + enum class LabelPosition { + // Labels should be placed at the beginning of their blocks. + kLabelsAtBeginning, + // Labels should be placed at the end of their blocks. + kLabelsAtEnd, + // Labels should not be in the worklist. + kNoLabels, + // Only labels should be placed in the worklist. + kLabelsOnly, + }; + + ForwardDataFlowAnalysis(IRContext& context, LabelPosition label_position) + : DataFlowAnalysis(context), label_position_(label_position) {} + + protected: + // Initializes the worklist in reverse postorder, regardless of + // |is_first_iteration|. Labels are placed according to the label position + // specified in the constructor. + void InitializeWorklist(Function* function, bool is_first_iteration) override; + + // Enqueues the users and block successors of the given instruction. + // See |EnqueueUsers| and |EnqueueBlockSuccessors|. + void EnqueueSuccessors(Instruction* inst) override { + EnqueueUsers(inst); + EnqueueBlockSuccessors(inst); + } + + // Enqueues the users of the given instruction. + void EnqueueUsers(Instruction* inst); + + // Enqueues the labels of the successors of the block corresponding to the + // given label instruction. Does nothing for other instructions. + void EnqueueBlockSuccessors(Instruction* inst); + + private: + LabelPosition label_position_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DATAFLOW_H_ diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 03312468..76ca99eb 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -31,6 +31,7 @@ add_spvtools_unittest(TARGET opt control_dependence.cpp convert_relaxed_to_half_test.cpp copy_prop_array_test.cpp + dataflow.cpp dead_branch_elim_test.cpp dead_insert_elim_test.cpp dead_variable_elim_test.cpp diff --git a/test/opt/dataflow.cpp b/test/opt/dataflow.cpp new file mode 100644 index 00000000..4742015a --- /dev/null +++ b/test/opt/dataflow.cpp @@ -0,0 +1,225 @@ +// Copyright (c) 2021 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/opt/dataflow.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "opt/function_utils.h" +#include "source/opt/build_module.h" + +namespace spvtools { +namespace opt { +namespace { + +using DataFlowTest = ::testing::Test; + +// Simple analyses for testing: + +// Stores the result IDs of visited instructions in visit order. +struct VisitOrder : public ForwardDataFlowAnalysis { + std::vector visited_result_ids; + + VisitOrder(IRContext& context, LabelPosition label_position) + : ForwardDataFlowAnalysis(context, label_position) {} + + VisitResult Visit(Instruction* inst) override { + if (inst->HasResultId()) { + visited_result_ids.push_back(inst->result_id()); + } + return DataFlowAnalysis::VisitResult::kResultFixed; + } +}; + +// For each block, stores the set of blocks it can be preceded by. +// For example, with the following CFG: +// V-----------. +// -> 11 -> 12 -> 13 -> 15 +// \-> 14 ---^ +// +// The answer is: +// 11: 11, 12, 13 +// 12: 11, 12, 13 +// 13: 11, 12, 13 +// 14: 11, 12, 13 +// 15: 11, 12, 13, 14 +struct BackwardReachability : public ForwardDataFlowAnalysis { + std::map> reachable_from; + + BackwardReachability(IRContext& context) + : ForwardDataFlowAnalysis( + context, ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly) {} + + VisitResult Visit(Instruction* inst) override { + // Conditional branches can be enqueued from labels, so skip them. + if (inst->opcode() != SpvOpLabel) + return DataFlowAnalysis::VisitResult::kResultFixed; + uint32_t id = inst->result_id(); + VisitResult ret = DataFlowAnalysis::VisitResult::kResultFixed; + std::set& precedents = reachable_from[id]; + for (uint32_t pred : context().cfg()->preds(id)) { + bool pred_inserted = precedents.insert(pred).second; + if (pred_inserted) { + ret = DataFlowAnalysis::VisitResult::kResultChanged; + } + for (uint32_t block : reachable_from[pred]) { + bool inserted = precedents.insert(block).second; + if (inserted) { + ret = DataFlowAnalysis::VisitResult::kResultChanged; + } + } + } + return ret; + } + + void InitializeWorklist(Function* function, + bool is_first_iteration) override { + // Since successor function is exact, only need one pass. + if (is_first_iteration) { + ForwardDataFlowAnalysis::InitializeWorklist(function, true); + } + } +}; + +TEST_F(DataFlowTest, ReversePostOrder) { + // Note: labels and IDs are intentionally out of order. + // + // CFG: (order of branches is from bottom to top) + // V-----------. + // -> 50 -> 40 -> 20 -> 60 -> 70 + // \-> 30 ---^ + + // DFS tree with RPO numbering: + // -> 50[0] -> 40[1] -> 20[2] 60[4] -> 70[5] + // \-> 30[3] ---^ + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %6 = OpTypeBool + %5 = OpConstantTrue %6 + %2 = OpFunction %3 None %4 + %50 = OpLabel + %51 = OpUndef %6 + %52 = OpUndef %6 + OpBranch %40 + %70 = OpLabel + %69 = OpUndef %6 + OpReturn + %60 = OpLabel + %61 = OpUndef %6 + OpBranchConditional %5 %70 %40 + %30 = OpLabel + %29 = OpUndef %6 + OpBranch %60 + %20 = OpLabel + %21 = OpUndef %6 + OpBranch %60 + %40 = OpLabel + %39 = OpUndef %6 + OpBranchConditional %5 %30 %20 + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Function* function = spvtest::GetFunction(context->module(), 2); + + std::map> + expected_order; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly] = { + 50, 40, 20, 30, 60, 70, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtBeginning] = { + 50, 51, 52, 40, 39, 20, 21, 30, 29, 60, 61, 70, 69, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtEnd] = { + 51, 52, 50, 39, 40, 21, 20, 29, 30, 61, 60, 69, 70, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kNoLabels] = { + 51, 52, 39, 21, 29, 61, 69, + }; + + for (const auto& test_case : expected_order) { + VisitOrder analysis(*context, test_case.first); + analysis.Run(function); + EXPECT_EQ(test_case.second, analysis.visited_result_ids); + } +} + +TEST_F(DataFlowTest, BackwardReachability) { + // CFG: + // V-----------. + // -> 11 -> 12 -> 13 -> 15 + // \-> 14 ---^ + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %6 = OpTypeBool + %5 = OpConstantTrue %6 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranchConditional %5 %14 %13 + %13 = OpLabel + OpBranchConditional %5 %15 %11 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Function* function = spvtest::GetFunction(context->module(), 2); + + BackwardReachability analysis(*context); + analysis.Run(function); + + std::map> expected_result; + expected_result[11] = {11, 12, 13}; + expected_result[12] = {11, 12, 13}; + expected_result[13] = {11, 12, 13}; + expected_result[14] = {11, 12, 13}; + expected_result[15] = {11, 12, 13, 14}; + EXPECT_EQ(expected_result, analysis.reachable_from); +} + +} // namespace +} // namespace opt +} // namespace spvtools -- cgit v1.2.3 From de69f32e8962ecc4e604bfc125da41d00a7a1ca8 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Tue, 10 Aug 2021 07:31:17 -0600 Subject: spirv-opt: Add handling of vulkan debug info to DebugInfoManager (#4423) Co-authored-by: baldurk --- source/opt/constants.cpp | 6 + source/opt/constants.h | 3 + source/opt/debug_info_manager.cpp | 299 ++++++++++++++++++++++++-------------- source/opt/debug_info_manager.h | 16 +- 4 files changed, 211 insertions(+), 113 deletions(-) diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index 19ca6008..a3dac5d7 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp @@ -432,6 +432,12 @@ uint32_t ConstantManager::GetSIntConst(int32_t val) { return GetDefiningInstruction(c)->result_id(); } +uint32_t ConstantManager::GetUIntConst(uint32_t val) { + Type* uint_type = context()->get_type_mgr()->GetUIntType(); + const Constant* c = GetConstant(uint_type, {val}); + return GetDefiningInstruction(c)->result_id(); +} + std::vector Constant::GetVectorComponents( analysis::ConstantManager* const_mgr) const { std::vector components; diff --git a/source/opt/constants.h b/source/opt/constants.h index 95d984fc..52bd809a 100644 --- a/source/opt/constants.h +++ b/source/opt/constants.h @@ -642,6 +642,9 @@ class ConstantManager { // Returns the id of a 32-bit signed integer constant with value |val|. uint32_t GetSIntConst(int32_t val); + // Returns the id of a 32-bit unsigned integer constant with value |val|. + uint32_t GetUIntConst(uint32_t val); + private: // Creates a Constant instance with the given type and a vector of constant // defining words. Returns a unique pointer to the created Constant instance diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index e782641f..2e8e1327 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -18,12 +18,15 @@ #include "source/opt/ir_context.h" -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 & NonSemantic.Vulkan.DebugInfo.100 +// extension instructions. static const uint32_t kOpLineOperandLineIndex = 1; static const uint32_t kLineOperandIndexDebugFunction = 7; static const uint32_t kLineOperandIndexDebugLexicalBlock = 5; static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4; +static const uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5; static const uint32_t kDebugFunctionOperandParentIndex = 9; static const uint32_t kDebugTypeCompositeOperandParentIndex = 9; static const uint32_t kDebugLexicalBlockOperandParentIndex = 7; @@ -46,8 +49,8 @@ namespace { void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { assert(dbg_inlined_at); - assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInlinedAt); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) { dbg_inlined_at->AddOperand( {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}}); @@ -59,8 +62,8 @@ void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { assert(dbg_inlined_at); - assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInlinedAt); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) return kNoInlinedAt; return dbg_inlined_at->GetSingleWordOperand( @@ -68,8 +71,7 @@ uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { } bool IsEmptyDebugExpression(Instruction* instr) { - return instr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugExpression && + return (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression) && instr->NumOperands() == kDebugExpressOperandOperationIndex; } @@ -79,43 +81,63 @@ DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) { AnalyzeDebugInsts(*c->module()); } +uint32_t DebugInfoManager::GetDbgSetImportId() { + uint32_t setId = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + if (setId == 0) { + setId = + context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo(); + } + return setId; +} + Instruction* DebugInfoManager::GetDbgInst(uint32_t id) { auto dbg_inst_it = id_to_dbg_inst_.find(id); return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second; } void DebugInfoManager::RegisterDbgInst(Instruction* inst) { - assert( - inst->NumInOperands() != 0 && - context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == - inst->GetInOperand(0).words[0] && - "Given instruction is not a debug instruction"); + assert(inst->NumInOperands() != 0 && + (GetDbgSetImportId() == inst->GetInOperand(0).words[0]) && + "Given instruction is not a debug instruction"); id_to_dbg_inst_[inst->result_id()] = inst; } void DebugInfoManager::RegisterDbgFunction(Instruction* inst) { - assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction && - "inst is not a DebugFunction"); - auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); - // Do not register function that has been optimized away - auto fn_inst = GetDbgInst(fn_id); - if (fn_inst != nullptr) { - assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInfoNone); - return; + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); + // Do not register function that has been optimized away. + auto fn_inst = GetDbgInst(fn_id); + if (fn_inst != nullptr) { + assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInfoNone); + return; + } + assert( + fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunction for a function that already has DebugFunction"); + fn_id_to_dbg_fn_[fn_id] = inst; + } else if (inst->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + auto fn_id = inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + auto fn_inst = GetDbgInst(inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandDebugFunctionIndex)); + assert(fn_inst && fn_inst->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugFunction); + assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunctionDefinition for a function that already has " + "DebugFunctionDefinition"); + fn_id_to_dbg_fn_[fn_id] = fn_inst; + } else { + assert(false && "inst is not a DebugFunction"); } - assert( - fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && - "Register DebugFunction for a function that already has DebugFunction"); - fn_id_to_dbg_fn_[fn_id] = inst; } void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare) { - assert(dbg_declare->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare || - dbg_declare->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugValue); + assert(dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugValue); auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id); if (dbg_decl_itr == var_id_to_dbg_decl_.end()) { var_id_to_dbg_decl_[var_id] = {dbg_declare}; @@ -126,27 +148,36 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, const DebugScope& scope) { - if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == - 0) - return kNoInlinedAt; + uint32_t setId = GetDbgSetImportId(); + + if (setId == 0) return kNoInlinedAt; + + spv_operand_type_t line_number_type = + spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER; + + // In NonSemantic.Vulkan.DebugInfo.100, all constants are IDs of OpConstant, + // not literals. + if (setId == + context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) + line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID; uint32_t line_number = 0; if (line == nullptr) { auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope()); if (lexical_scope_inst == nullptr) return kNoInlinedAt; - OpenCLDebugInfo100Instructions debug_opcode = - lexical_scope_inst->GetOpenCL100DebugOpcode(); + CommonDebugInfoInstructions debug_opcode = + lexical_scope_inst->GetCommonDebugOpcode(); switch (debug_opcode) { - case OpenCLDebugInfo100DebugFunction: + case CommonDebugInfoDebugFunction: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugFunction); break; - case OpenCLDebugInfo100DebugLexicalBlock: + case CommonDebugInfoDebugLexicalBlock: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugLexicalBlock); break; - case OpenCLDebugInfo100DebugTypeComposite: - case OpenCLDebugInfo100DebugCompilationUnit: + case CommonDebugInfoDebugTypeComposite: + case CommonDebugInfoDebugCompilationUnit: assert(false && "DebugTypeComposite and DebugCompilationUnit are lexical " "scopes, but we inline functions into a function or a block " @@ -161,6 +192,13 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, } } else { line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); + + // If we need the line number as an ID, generate that constant now. + if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID) { + uint32_t line_id = + context()->get_constant_mgr()->GetUIntConst(line_number); + line_number = line_id; + } } uint32_t result_id = context()->TakeNextId(); @@ -168,13 +206,10 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {setId}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugInlinedAt)}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {static_cast(CommonDebugInfoDebugInlinedAt)}}, + {line_number_type, {line_number}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}}, })); // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt @@ -257,19 +292,34 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { if (deref_operation_ != nullptr) return deref_operation_; uint32_t result_id = context()->TakeNextId(); - std::unique_ptr deref_operation(new Instruction( - context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), - result_id, - { - {SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, - {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugOperation)}}, - {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, - {static_cast(OpenCLDebugInfo100Deref)}}, - })); + std::unique_ptr deref_operation; + + if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + deref_operation = std::unique_ptr(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, + {static_cast(OpenCLDebugInfo100Deref)}}, + })); + } else { + uint32_t deref_id = context()->get_constant_mgr()->GetUIntConst( + NonSemanticVulkanDebugInfo100Deref); + + deref_operation = std::unique_ptr( + new Instruction(context(), SpvOpExtInst, + context()->get_type_mgr()->GetVoidTypeId(), result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast( + NonSemanticVulkanDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_ID, {deref_id}}, + })); + } // Add to the front of |ext_inst_debuginfo_|. deref_operation_ = @@ -283,8 +333,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { } Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { - assert(dbg_expr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugExpression); + assert(dbg_expr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression); std::unique_ptr deref_expr(dbg_expr->Clone(context())); deref_expr->SetResultId(context()->TakeNextId()); deref_expr->InsertOperand( @@ -306,12 +355,9 @@ Instruction* DebugInfoManager::GetDebugInfoNone() { context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugInfoNone)}}, + {static_cast(CommonDebugInfoDebugInfoNone)}}, })); // Add to the front of |ext_inst_debuginfo_|. @@ -333,12 +379,9 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() { context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugExpression)}}, + {static_cast(CommonDebugInfoDebugExpression)}}, })); // Add to the front of |ext_inst_debuginfo_|. @@ -355,8 +398,7 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() { Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) { auto* inlined_at = GetDbgInst(dbg_inlined_at_id); if (inlined_at == nullptr) return nullptr; - if (inlined_at->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugInlinedAt) { + if (inlined_at->GetCommonDebugOpcode() != CommonDebugInfoDebugInlinedAt) { return nullptr; } return inlined_at; @@ -403,23 +445,23 @@ bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) { auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope); assert(dbg_scope_itr != id_to_dbg_inst_.end()); - OpenCLDebugInfo100Instructions debug_opcode = - dbg_scope_itr->second->GetOpenCL100DebugOpcode(); + CommonDebugInfoInstructions debug_opcode = + dbg_scope_itr->second->GetCommonDebugOpcode(); uint32_t parent_scope = kNoDebugScope; switch (debug_opcode) { - case OpenCLDebugInfo100DebugFunction: + case CommonDebugInfoDebugFunction: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugFunctionOperandParentIndex); break; - case OpenCLDebugInfo100DebugLexicalBlock: + case CommonDebugInfoDebugLexicalBlock: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugLexicalBlockOperandParentIndex); break; - case OpenCLDebugInfo100DebugTypeComposite: + case CommonDebugInfoDebugTypeComposite: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugTypeCompositeOperandParentIndex); break; - case OpenCLDebugInfo100DebugCompilationUnit: + case CommonDebugInfoDebugCompilationUnit: // DebugCompilationUnit does not have a parent scope. break; default: @@ -513,8 +555,7 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( std::unique_ptr dbg_val(dbg_decl->Clone(context())); dbg_val->SetResultId(context()->TakeNextId()); - dbg_val->SetInOperand(kExtInstInstructionInIdx, - {OpenCLDebugInfo100DebugValue}); + dbg_val->SetInOperand(kExtInstInstructionInIdx, {CommonDebugInfoDebugValue}); dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); dbg_val->SetOperand(kDebugValueOperandExpressionIndex, {GetEmptyDebugExpression()->result_id()}); @@ -532,9 +573,20 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( return added_dbg_val; } +uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) { + assert(inst->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugOperation && + "inst must be Vulkan DebugOperation"); + return context() + ->get_constant_mgr() + ->GetConstantFromInst(context()->get_def_use_mgr()->GetDef( + inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex))) + ->GetU32(); +} + uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( Instruction* inst) { - if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0; + if (inst->GetCommonDebugOpcode() != CommonDebugInfoDebugValue) return 0; auto* expr = GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); @@ -544,9 +596,19 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( auto* operation = GetDbgInst( expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex)); if (operation == nullptr) return 0; - if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != - OpenCLDebugInfo100Deref) { - return 0; + + // OpenCL.DebugInfo.100 contains a literal for the operation, Vulkan uses an + // OpConstant. + if (inst->IsOpenCL100DebugInstr()) { + if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != + OpenCLDebugInfo100Deref) { + return 0; + } + } else { + uint32_t operation_const = GetVulkanDebugOperation(operation); + if (operation_const != NonSemanticVulkanDebugInfo100Deref) { + return 0; + } } uint32_t var_id = @@ -567,8 +629,8 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( } bool DebugInfoManager::IsDebugDeclare(Instruction* instr) { - if (!instr->IsOpenCL100DebugInstr()) return false; - return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || + if (!instr->IsCommonDebugInstr()) return false; + return instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || GetVariableIdOfDebugValueUsedForDeclare(instr) != 0; } @@ -615,14 +677,13 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { users.insert(inst); } - if (!inst->IsOpenCL100DebugInstr()) return; + if (!inst->IsCommonDebugInstr()) return; RegisterDbgInst(inst); - if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { - assert(GetDebugFunction(inst->GetSingleWordOperand( - kDebugFunctionOperandFunctionIndex)) == nullptr && - "Two DebugFunction instruction exists for a single OpFunction."); + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction || + inst->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { RegisterDbgFunction(inst); } @@ -633,8 +694,17 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { deref_operation_ = inst; } + if (deref_operation_ == nullptr && + inst->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(inst); + if (operation_const == NonSemanticVulkanDebugInfo100Deref) { + deref_operation_ = inst; + } + } + if (debug_info_none_inst_ == nullptr && - inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + inst->GetCommonDebugOpcode() == CommonDebugInfoDebugInfoNone) { debug_info_none_inst_ = inst; } @@ -642,7 +712,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { empty_debug_expr_inst_ = inst; } - if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { uint32_t var_id = inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); RegisterDbgDeclare(var_id, inst); @@ -655,8 +725,8 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { void DebugInfoManager::ConvertDebugGlobalToLocalVariable( Instruction* dbg_global_var, Instruction* local_var) { - if (dbg_global_var->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugGlobalVariable) { + if (dbg_global_var->GetCommonDebugOpcode() != + CommonDebugInfoDebugGlobalVariable) { return; } assert(local_var->opcode() == SpvOpVariable || @@ -664,7 +734,7 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable( // Convert |dbg_global_var| to DebugLocalVariable dbg_global_var->SetInOperand(kExtInstInstructionInIdx, - {OpenCLDebugInfo100DebugLocalVariable}); + {CommonDebugInfoDebugLocalVariable}); auto flags = dbg_global_var->GetSingleWordOperand( kDebugGlobalVariableOperandFlagsIndex); for (uint32_t i = dbg_global_var->NumInOperands() - 1; @@ -680,12 +750,9 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable( context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), context()->TakeNextId(), { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugDeclare)}}, + {static_cast(CommonDebugInfoDebugDeclare)}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_global_var->result_id()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}}, @@ -713,7 +780,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (empty_debug_expr_inst_ != nullptr && empty_debug_expr_inst_->PreviousNode() != nullptr && - empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + empty_debug_expr_inst_->PreviousNode()->IsCommonDebugInstr()) { empty_debug_expr_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } @@ -722,7 +789,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (debug_info_none_inst_ != nullptr && debug_info_none_inst_->PreviousNode() != nullptr && - debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + debug_info_none_inst_->PreviousNode()->IsCommonDebugInstr()) { debug_info_none_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } @@ -740,7 +807,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { inlinedat_id_to_users_itr->second.erase(instr); } - if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) { + if (instr == nullptr || !instr->IsCommonDebugInstr()) { return; } @@ -751,9 +818,15 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); } + if (instr->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugFunction) { + auto fn_id = instr->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + fn_id_to_dbg_fn_.erase(fn_id); + } - if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || - instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + instr->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { auto var_or_value_id = instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id); @@ -767,6 +840,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { + // OpenCL.DebugInfo.100 contains the operation as a literal operand, in + // Vulkan it's referenced as an OpConstant. if (instr != &*dbg_instr_itr && dbg_instr_itr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && @@ -775,6 +850,15 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { OpenCLDebugInfo100Deref) { deref_operation_ = &*dbg_instr_itr; break; + } else if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetVulkan100DebugOpcode() == + NonSemanticVulkanDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr); + + if (operation_const == NonSemanticVulkanDebugInfo100Deref) { + deref_operation_ = &*dbg_instr_itr; + break; + } } } } @@ -784,9 +868,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { - if (instr != &*dbg_instr_itr && - dbg_instr_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInfoNone) { + if (instr != &*dbg_instr_itr && dbg_instr_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugInfoNone) { debug_info_none_inst_ = &*dbg_instr_itr; break; } diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h index 776e9baa..679ae138 100644 --- a/source/opt/debug_info_manager.h +++ b/source/opt/debug_info_manager.h @@ -67,8 +67,8 @@ class DebugInlinedAtContext { std::unordered_map callee_inlined_at2chain_; }; -// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension -// instructions. +// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and +// NonSemantic.Vulkan.DebugInfo.100 extension instructions. class DebugInfoManager { public: // Constructs a debug information manager from the given |context|. @@ -85,7 +85,7 @@ class DebugInfoManager { return !(lhs == rhs); } - // Analyzes OpenCL.DebugInfo.100 instruction |dbg_inst|. + // Analyzes DebugInfo instruction |dbg_inst|. void AnalyzeDebugInst(Instruction* dbg_inst); // Creates new DebugInlinedAt and returns its id. Its line operand is the @@ -164,6 +164,9 @@ class DebugInfoManager { // Erases |instr| from data structures of this class. void ClearDebugInfo(Instruction* instr); + // Return the opcode for the Vulkan DebugOperation inst + uint32_t GetVulkanDebugOperation(Instruction* inst); + // Returns the id of Value operand if |inst| is DebugValue who has Deref // operation and its Value operand is a result id of OpVariable with // Function storage class. Otherwise, returns 0. @@ -190,10 +193,13 @@ class DebugInfoManager { private: IRContext* context() { return context_; } - // Analyzes OpenCL.DebugInfo.100 instructions in the given |module| and + // Analyzes DebugInfo instructions in the given |module| and // populates data structures in this class. void AnalyzeDebugInsts(Module& module); + // Get the DebugInfo ExtInstImport Id, or 0 if no DebugInfo is available. + uint32_t GetDbgSetImportId(); + // Returns the debug instruction whose id is |id|. Returns |nullptr| if one // does not exists. Instruction* GetDbgInst(uint32_t id); @@ -230,7 +236,7 @@ class DebugInfoManager { IRContext* context_; - // Mapping from ids of OpenCL.DebugInfo.100 extension instructions + // Mapping from ids of DebugInfo extension instructions. // to their Instruction instances. std::unordered_map id_to_dbg_inst_; -- cgit v1.2.3 From 54524ffa6a1b5ab173ebff89fb31e4a21d365477 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 12 Aug 2021 10:34:29 -0400 Subject: Update SPIRV-Headers (#4463) * Add SpvBuiltInCurrentRayTimeNV to unvalidated builtins --- DEPS | 2 +- source/val/validate_builtins.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/DEPS b/DEPS index c4c41f0f..92c67e05 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382', 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683', - 'spirv_headers_revision': 'cf653e4ca4858583802b0d1656bc934edff6bd7f', + 'spirv_headers_revision': 'e71feddb3f17c5586ff7f4cfb5ed1258b800574b', } deps = { diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index 3c9df9fc..9c737ec0 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp @@ -4190,6 +4190,7 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInMeshViewIndicesNV: case SpvBuiltInBaryCoordNV: case SpvBuiltInBaryCoordNoPerspNV: + case SpvBuiltInCurrentRayTimeNV: // No validation rules (for the moment). break; -- cgit v1.2.3 From 00b106e769843ad4076b4d0fffb3ab89ec7a1a97 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Fri, 13 Aug 2021 13:00:00 -0400 Subject: Limit location validation (#4467) Fixes #4464 * Add a limit to only check up to 4096 locations to prevent excessive memory consumption * still fully validates all reasonable inputs --- source/val/validate_interfaces.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index d16d48e2..298e4e2f 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -27,6 +27,10 @@ namespace spvtools { namespace val { namespace { +// Limit the number of checked locations to 4096. Multiplied by 4 to represent +// all the components. This limit is set to be well beyond practical use cases. +const uint32_t kMaxLocations = 4096 * 4; + // Returns true if \c inst is an input or output variable. bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) { if (is_spv_1_4) { @@ -347,6 +351,11 @@ spv_result_t GetLocationsForVariable( uint32_t num_components = NumConsumedComponents(_, sub_type); uint32_t array_location = location + (num_locations * array_idx); uint32_t start = array_location * 4; + if (kMaxLocations <= start) { + // Too many locations, give up. + break; + } + uint32_t end = (array_location + num_locations) * 4; if (num_components != 0) { start += component; @@ -416,6 +425,11 @@ spv_result_t GetLocationsForVariable( } uint32_t start = location * 4; + if (kMaxLocations <= start) { + // Too many locations, give up. + continue; + } + uint32_t end = (location + num_locations) * 4; if (num_components != 0) { start += component; -- cgit v1.2.3 From 881001070a8f3baf1ee5d7ef3cbc6e76e4015ce0 Mon Sep 17 00:00:00 2001 From: 5265644D61736F6E <61918637+5265644D61736F6E@users.noreply.github.com> Date: Mon, 16 Aug 2021 09:44:22 -0400 Subject: fix SIGSEGV when reading from a non-existant file (#4453) When a tool (tested with spirv-dis and spirv-as) runs on a file that does not exist, it notifies the user and tries to close the stream that was never successfully opened. --- tools/io.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/io.h b/tools/io.h index aff9eabd..83a85c1d 100644 --- a/tools/io.h +++ b/tools/io.h @@ -89,7 +89,7 @@ bool ReadBinaryFile(const char* filename, std::vector* data) { ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); - if (use_file) fclose(fp); + if (use_file && fp) fclose(fp); return succeeded; } @@ -111,7 +111,7 @@ bool ReadTextFile(const char* filename, std::vector* data) { ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); - if (use_file) fclose(fp); + if (use_file && fp) fclose(fp); return succeeded; } -- cgit v1.2.3 From 869a550d26ba5eb8c3b5ae57b15ceea2554494ed Mon Sep 17 00:00:00 2001 From: Nicolas Capens Date: Mon, 16 Aug 2021 09:56:05 -0400 Subject: Don't fold unsigned divides of an constant and a negation (#4457) Negating an unsigned constant results in its two's complement which is still interpreted as unsigned. For example -2u becomes 4294967294u. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4456 --- source/opt/folding_rules.cpp | 7 ++----- test/opt/fold_test.cpp | 28 +++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index e3e926c3..6ae078fb 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -967,12 +967,11 @@ FoldingRule MergeDivMulArithmetic() { // Fold divides of a constant and a negation. // Cases: // (-x) / 2 = x / -2 -// 2 / (-x) = 2 / -x +// 2 / (-x) = -2 / x FoldingRule MergeDivNegateArithmetic() { return [](IRContext* context, Instruction* inst, const std::vector& constants) { - assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv || - inst->opcode() == SpvOpUDiv); + assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv); analysis::ConstantManager* const_mgr = context->get_constant_mgr(); const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); @@ -2572,8 +2571,6 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpStore].push_back(StoringUndef()); - rules_[SpvOpUDiv].push_back(MergeDivNegateArithmetic()); - rules_[SpvOpVectorShuffle].push_back(VectorShuffleFeedingShuffle()); rules_[SpvOpImageSampleImplicitLod].push_back(UpdateImageOperands()); diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 8457bbfe..da5b017d 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -5818,7 +5818,33 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%5 = OpFDiv %float %4 %2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 5, true) + 5, true), + // Test case 16: Do not merge udiv of snegate + // (-x) / 2u + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpSNegate %uint %2\n" + + "%4 = OpUDiv %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 17: Do not merge udiv of snegate + // 2u / (-x) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpSNegate %uint %2\n" + + "%4 = OpUDiv %uint %uint_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false) )); INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, -- cgit v1.2.3 From 2c829c4155610eb3a6e2ff87462de4d01358ec87 Mon Sep 17 00:00:00 2001 From: Nicolas Capens Date: Mon, 16 Aug 2021 14:11:38 -0400 Subject: Fix early-out for Clamp constant folding (#4461) Only the first two operands were tested for constness, missing the third one. Since the FoldFPBinaryOp() at the end of FoldClamp1() returns null when not both of its operands are constant, this doesn't change any behavior, but it avoids some needless work. Also the comment for FoldClamp2() was fixed. --- source/opt/const_folding_rules.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index d262a7ec..515a3ed5 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp @@ -1002,7 +1002,7 @@ const analysis::Constant* FoldClamp1( "Expecting a GLSLstd450 extended instruction."); // Make sure all Clamp operands are constants. - for (uint32_t i = 1; i < 3; i++) { + for (uint32_t i = 1; i < 4; i++) { if (constants[i] == nullptr) { return nullptr; } @@ -1017,7 +1017,7 @@ const analysis::Constant* FoldClamp1( context); } -// Fold a clamp instruction when |x >= min_val|. +// Fold a clamp instruction when |x <= min_val|. const analysis::Constant* FoldClamp2( IRContext* context, Instruction* inst, const std::vector& constants) { -- cgit v1.2.3 From b2e36b67eca055e4338d260cf5bc23c12420494d Mon Sep 17 00:00:00 2001 From: alan-baker Date: Mon, 16 Aug 2021 18:23:10 -0400 Subject: Disallow loading a runtime-sized array (#4473) * Disallow loading a runtime-sized array Fixes #4472 * Disallow loading a runtime-sized array or a composite containing one * Refactor type traversal into a separate function used by both runtime array checks and sized int/float checks * Update invalid tests --- source/val/validate_memory.cpp | 6 +++ source/val/validation_state.cpp | 59 ++++++++++++++------- source/val/validation_state.h | 11 ++++ test/opt/eliminate_dead_member_test.cpp | 1 - test/val/val_composites_test.cpp | 23 ++++----- test/val/val_memory_test.cpp | 90 ++++++++++++++++++++++++++++++++- 6 files changed, 159 insertions(+), 31 deletions(-) diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index a4bc0fab..a7b0f82d 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -893,6 +893,12 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { << "'s type."; } + if (!_.options()->before_hlsl_legalization && + _.ContainsRuntimeArray(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot load a runtime-sized array"; + } + if (auto error = CheckMemoryAccess(_, inst, 3)) return error; if (_.HasCapability(SpvCapabilityShader) && diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 52821636..c9ac3ae7 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -1262,16 +1262,13 @@ const Instruction* ValidationState_t::TracePointer( return base_ptr; } -bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, - uint32_t width) const { - if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false; - +bool ValidationState_t::ContainsType( + uint32_t id, const std::function& f, + bool traverse_all_types) const { const auto inst = FindDef(id); if (!inst) return false; - if (inst->opcode() == type) { - return inst->GetOperandAs(1u) == width; - } + if (f(inst)) return true; switch (inst->opcode()) { case SpvOpTypeArray: @@ -1281,24 +1278,45 @@ bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, case SpvOpTypeImage: case SpvOpTypeSampledImage: case SpvOpTypeCooperativeMatrixNV: - return ContainsSizedIntOrFloatType(inst->GetOperandAs(1u), type, - width); + return ContainsType(inst->GetOperandAs(1u), f, + traverse_all_types); case SpvOpTypePointer: if (IsForwardPointer(id)) return false; - return ContainsSizedIntOrFloatType(inst->GetOperandAs(2u), type, - width); + if (traverse_all_types) { + return ContainsType(inst->GetOperandAs(2u), f, + traverse_all_types); + } + break; case SpvOpTypeFunction: - case SpvOpTypeStruct: { + case SpvOpTypeStruct: + if (inst->opcode() == SpvOpTypeFunction && !traverse_all_types) { + return false; + } for (uint32_t i = 1; i < inst->operands().size(); ++i) { - if (ContainsSizedIntOrFloatType(inst->GetOperandAs(i), type, - width)) + if (ContainsType(inst->GetOperandAs(i), f, + traverse_all_types)) { return true; + } } - return false; - } + break; default: - return false; + break; } + + return false; +} + +bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, + uint32_t width) const { + if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false; + + const auto f = [type, width](const Instruction* inst) { + if (inst->opcode() == type) { + return inst->GetOperandAs(1u) == width; + } + return false; + }; + return ContainsType(id, f); } bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { @@ -1313,6 +1331,13 @@ bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { return false; } +bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const { + const auto f = [](const Instruction* inst) { + return inst->opcode() == SpvOpTypeRuntimeArray; + }; + return ContainsType(id, f, /* traverse_all_types = */ false); +} + bool ValidationState_t::IsValidStorageClass( SpvStorageClass storage_class) const { if (spvIsVulkanEnv(context()->target_env)) { diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 57634bf4..2fe96621 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -595,6 +595,17 @@ class ValidationState_t { // 16-bit float that is not generally enabled for use. bool ContainsLimitedUseIntOrFloatType(uint32_t id) const; + // Returns true if |id| is a type that contains a runtime-sized array. + // Does not consider a pointers as contains the array. + bool ContainsRuntimeArray(uint32_t id) const; + + // Generic type traversal. + // Only traverse pointers and functions if |traverse_all_types| is true. + // Recursively tests |f| against the type hierarchy headed by |id|. + bool ContainsType(uint32_t id, + const std::function& f, + bool traverse_all_types = true) const; + // Gets value from OpConstant and OpSpecConstant as uint64. // Returns false on failure (no instruction, wrong instruction, not int). bool GetConstantValUint64(uint32_t id, uint64_t* val) const; diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp index 7728782e..78874330 100644 --- a/test/opt/eliminate_dead_member_test.cpp +++ b/test/opt/eliminate_dead_member_test.cpp @@ -576,7 +576,6 @@ TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateArrayLength) { %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform %main = OpFunction %void None %9 %10 = OpLabel - %11 = OpLoad %type__Globals %_Globals %12 = OpArrayLength %uint %_Globals 2 OpReturn OpFunctionEnd diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp index bf7caa9f..507ee889 100644 --- a/test/val/val_composites_test.cpp +++ b/test/val/val_composites_test.cpp @@ -86,10 +86,10 @@ OpCapability Float64 %f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 %f32vec2arr3 = OpTypeArray %f32vec2 %u32_3 -%f32vec2rarr = OpTypeRuntimeArray %f32vec2 +%f32vec2arr2 = OpTypeArray %f32vec2 %u32_2 %f32u32struct = OpTypeStruct %f32 %u32 -%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2rarr %f32u32struct +%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2arr2 %f32u32struct %ptr_big_struct = OpTypePointer Uniform %big_struct %var_big_struct = OpVariable %ptr_big_struct Uniform @@ -150,7 +150,6 @@ OpMemoryModel Logical GLSL450 ; uniform blockName { ; S s; ; bool cond; -; RunTimeArray arr; ; } %f32arr = OpTypeRuntimeArray %float @@ -161,7 +160,7 @@ OpMemoryModel Logical GLSL450 %_ptr_Function_vec4 = OpTypePointer Function %v4float %_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float %struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3 -%struct_blockName = OpTypeStruct %struct_s %int %f32arr +%struct_blockName = OpTypeStruct %struct_s %int %_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName %_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s %_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3 @@ -644,8 +643,8 @@ TEST_F(ValidateComposites, CompositeExtractSuccess) { %val12 = OpCompositeExtract %f32 %struct 2 2 1 %val13 = OpCompositeExtract %f32vec2 %struct 3 2 %val14 = OpCompositeExtract %f32 %struct 3 2 1 -%val15 = OpCompositeExtract %f32vec2 %struct 4 100 -%val16 = OpCompositeExtract %f32 %struct 4 1000 1 +%val15 = OpCompositeExtract %f32vec2 %struct 4 1 +%val16 = OpCompositeExtract %f32 %struct 4 0 1 %val17 = OpCompositeExtract %f32 %struct 5 0 %val18 = OpCompositeExtract %u32 %struct 5 1 )"; @@ -868,8 +867,8 @@ TEST_F(ValidateComposites, CompositeInsertSuccess) { %val12 = OpCompositeInsert %big_struct %f32_3 %struct 2 2 1 %val13 = OpCompositeInsert %big_struct %f32vec2_01 %struct 3 2 %val14 = OpCompositeInsert %big_struct %f32_3 %struct 3 2 1 -%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 100 -%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 1000 1 +%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 1 +%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 0 1 %val17 = OpCompositeInsert %big_struct %f32_3 %struct 5 0 %val18 = OpCompositeInsert %big_struct %u32_3 %struct 5 1 )"; @@ -1382,8 +1381,8 @@ TEST_F(ValidateComposites, CompositeExtractStructIndexOutOfBoundBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Index is out of bounds, can not find index 3 in the " - "structure '25'. This structure has 3 members. " - "Largest valid index is 2.")); + "structure '25'. This structure has 2 members. " + "Largest valid index is 1.")); } // Invalid. Index into a struct is larger than the number of struct members. @@ -1403,8 +1402,8 @@ TEST_F(ValidateComposites, CompositeInsertStructIndexOutOfBoundBad) { EXPECT_THAT( getDiagnosticString(), HasSubstr("Index is out of bounds, can not find index 3 in the structure " - " '25'. This structure has 3 members. Largest valid index " - "is 2.")); + " '25'. This structure has 2 members. Largest valid index " + "is 1.")); } // #1403: Ensure that the default spec constant value is not used to check the diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 9799b404..616a88f6 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -746,7 +746,7 @@ TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) { %float = OpTypeFloat 32 %uint = OpTypeInt 32 0 %_runtimearr_float = OpTypeRuntimeArray %float - %_struct_7 = OpTypeStruct %float %_runtimearr_float + %_struct_7 = OpTypeStruct %float %_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 %1 = OpFunction %void None %3 %9 = OpLabel @@ -4284,6 +4284,94 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateMemory, LoadRuntimeArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_block StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_rta %var %int_0 +%ld = OpLoad %rta %gep +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, LoadRuntimeArrayInStruct) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_block StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %block %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, LoadRuntimeArrayInArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%array = OpTypeArray %block %int_4 +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%ptr_array = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %array %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 57e1d8ebe3190c4ba23c1bee5147fe00c83ce358 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Wed, 18 Aug 2021 08:30:48 -0400 Subject: Add spirv-opt convert-to-sampled-image pass (#4340) convert-to-sampled-image pass converts images and/or samplers with given pairs of descriptor set and binding to sampled image. If a pair of an image and a sampler have the same pair of descriptor set and binding that is one of the given pairs, they will be converted to a sampled image. In addition, if only an image has the descriptor set and binding that is one of the given pairs, it will be converted to a sampled image as well. For example, when we have %a = OpLoad %type_2d_image %texture %b = OpLoad %type_sampler %sampler %combined = OpSampledImage %type_sampled_image %a %b %value = OpImageSampleExplicitLod %v4float %combined ... 1. If %texture and %sampler have the same descriptor set and binding %combine_texture_and_sampler = OpVaraible %ptr_type_sampled_image_Uniform ... %combined = OpLoad %type_sampled_image %combine_texture_and_sampler %value = OpImageSampleExplicitLod %v4float %combined ... 2. If %texture and %sampler have different pairs of descriptor set and binding %a = OpLoad %type_sampled_image %texture %extracted_image = OpImage %type_2d_image %a %b = OpLoad %type_sampler %sampler %combined = OpSampledImage %type_sampled_image %extracted_image %b %value = OpImageSampleExplicitLod %v4float %combined ... --- Android.mk | 1 + BUILD.gn | 2 + include/spirv-tools/optimizer.hpp | 12 + source/opt/CMakeLists.txt | 2 + source/opt/convert_to_sampled_image_pass.cpp | 437 +++++++++++++++++++++++++++ source/opt/convert_to_sampled_image_pass.h | 207 +++++++++++++ source/opt/optimizer.cpp | 27 ++ source/opt/passes.h | 1 + test/opt/CMakeLists.txt | 1 + test/opt/convert_to_sampled_image_test.cpp | 353 ++++++++++++++++++++++ tools/opt/opt.cpp | 9 + 11 files changed, 1052 insertions(+) create mode 100644 source/opt/convert_to_sampled_image_pass.cpp create mode 100644 source/opt/convert_to_sampled_image_pass.h create mode 100644 test/opt/convert_to_sampled_image_test.cpp diff --git a/Android.mk b/Android.mk index ff856fe9..2bd72ae3 100644 --- a/Android.mk +++ b/Android.mk @@ -89,6 +89,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/const_folding_rules.cpp \ source/opt/constants.cpp \ source/opt/control_dependence.cpp \ + source/opt/convert_to_sampled_image_pass.cpp \ source/opt/convert_to_half_pass.cpp \ source/opt/copy_prop_arrays.cpp \ source/opt/dataflow.cpp \ diff --git a/BUILD.gn b/BUILD.gn index 036a0a1c..8a0e5014 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -608,6 +608,8 @@ static_library("spvtools_opt") { "source/opt/constants.h", "source/opt/control_dependence.cpp", "source/opt/control_dependence.h", + "source/opt/convert_to_sampled_image_pass.cpp", + "source/opt/convert_to_sampled_image_pass.h", "source/opt/convert_to_half_pass.cpp", "source/opt/convert_to_half_pass.h", "source/opt/copy_prop_arrays.cpp", diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 700f59f7..b1442dcf 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "libspirv.hpp" @@ -27,6 +28,7 @@ namespace spvtools { namespace opt { class Pass; +struct DescriptorSetAndBinding; } // C++ interface for SPIR-V optimization functionalities. It wraps the context @@ -854,6 +856,16 @@ Optimizer::PassToken CreateAmdExtToKhrPass(); // propagated into their final positions. Optimizer::PassToken CreateInterpolateFixupPass(); +// Creates a convert-to-sampled-image pass to convert images and/or +// samplers with given pairs of descriptor set and binding to sampled image. +// If a pair of an image and a sampler have the same pair of descriptor set and +// binding that is one of the given pairs, they will be converted to a sampled +// image. In addition, if only an image has the descriptor set and binding that +// is one of the given pairs, it will be converted to a sampled image as well. +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index b6027c08..63af5c1d 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -28,6 +28,7 @@ set(SPIRV_TOOLS_OPT_SOURCES const_folding_rules.h constants.h control_dependence.h + convert_to_sampled_image_pass.h convert_to_half_pass.h copy_prop_arrays.h dataflow.h @@ -136,6 +137,7 @@ set(SPIRV_TOOLS_OPT_SOURCES const_folding_rules.cpp constants.cpp control_dependence.cpp + convert_to_sampled_image_pass.cpp convert_to_half_pass.cpp copy_prop_arrays.cpp dataflow.cpp diff --git a/source/opt/convert_to_sampled_image_pass.cpp b/source/opt/convert_to_sampled_image_pass.cpp new file mode 100644 index 00000000..e84d3578 --- /dev/null +++ b/source/opt/convert_to_sampled_image_pass.cpp @@ -0,0 +1,437 @@ +// Copyright (c) 2021 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/opt/convert_to_sampled_image_pass.h" + +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/util/make_unique.h" +#include "source/util/parse_number.h" + +namespace spvtools { +namespace opt { + +using VectorOfDescriptorSetAndBindingPairs = + std::vector; +using DescriptorSetBindingToInstruction = + ConvertToSampledImagePass::DescriptorSetBindingToInstruction; + +namespace { + +using utils::ParseNumber; + +// Returns true if the given char is ':', '\0' or considered as blank space +// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' '). +bool IsSeparator(char ch) { + return std::strchr(":\0", ch) || std::isspace(ch) != 0; +} + +// Reads characters starting from |str| until it meets a separator. Parses a +// number from the characters and stores it into |number|. Returns the pointer +// to the separator if it succeeds. Otherwise, returns nullptr. +const char* ParseNumberUntilSeparator(const char* str, uint32_t* number) { + const char* number_begin = str; + while (!IsSeparator(*str)) str++; + const char* number_end = str; + std::string number_in_str(number_begin, number_end - number_begin); + if (!utils::ParseNumber(number_in_str.c_str(), number)) { + // The descriptor set is not a valid uint32 number. + return nullptr; + } + return str; +} + +// Returns id of the image type used for the sampled image type of +// |sampled_image|. +uint32_t GetImageTypeOfSampledImage(analysis::TypeManager* type_mgr, + Instruction* sampled_image) { + auto* sampled_image_type = + type_mgr->GetType(sampled_image->type_id())->AsSampledImage(); + return type_mgr->GetTypeInstruction(sampled_image_type->image_type()); +} + +// Finds the instruction whose id is |inst_id|. Follows the operand of +// OpCopyObject recursively if the opcode of the instruction is OpCopyObject +// and returns the first instruction that does not have OpCopyObject as opcode. +Instruction* GetNonCopyObjectDef(analysis::DefUseManager* def_use_mgr, + uint32_t inst_id) { + Instruction* inst = def_use_mgr->GetDef(inst_id); + while (inst->opcode() == SpvOpCopyObject) { + inst_id = inst->GetSingleWordInOperand(0u); + inst = def_use_mgr->GetDef(inst_id); + } + return inst; +} + +} // namespace + +bool ConvertToSampledImagePass::GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const { + auto* decoration_manager = context()->get_decoration_mgr(); + bool found_descriptor_set_to_convert = false; + bool found_binding_to_convert = false; + for (auto decorate : + decoration_manager->GetDecorationsFor(inst.result_id(), false)) { + uint32_t decoration = decorate->GetSingleWordInOperand(1u); + if (decoration == SpvDecorationDescriptorSet) { + if (found_descriptor_set_to_convert) { + assert(false && "A resource has two OpDecorate for the descriptor set"); + return false; + } + descriptor_set_binding->descriptor_set = + decorate->GetSingleWordInOperand(2u); + found_descriptor_set_to_convert = true; + } else if (decoration == SpvDecorationBinding) { + if (found_binding_to_convert) { + assert(false && "A resource has two OpDecorate for the binding"); + return false; + } + descriptor_set_binding->binding = decorate->GetSingleWordInOperand(2u); + found_binding_to_convert = true; + } + } + return found_descriptor_set_to_convert && found_binding_to_convert; +} + +bool ConvertToSampledImagePass::ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding_pairs_.find(descriptor_set_binding) != + descriptor_set_binding_pairs_.end(); +} + +const analysis::Type* ConvertToSampledImagePass::GetVariableType( + const Instruction& variable) const { + if (variable.opcode() != SpvOpVariable) return nullptr; + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return nullptr; + + return pointer_type->pointee_type(); +} + +SpvStorageClass ConvertToSampledImagePass::GetStorageClass( + const Instruction& variable) const { + assert(variable.opcode() == SpvOpVariable); + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return SpvStorageClassMax; + + return pointer_type->storage_class(); +} + +bool ConvertToSampledImagePass::CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const { + for (auto& inst : context()->types_values()) { + const auto* variable_type = GetVariableType(inst); + if (variable_type == nullptr) continue; + + DescriptorSetAndBinding descriptor_set_binding; + if (!GetDescriptorSetBinding(inst, &descriptor_set_binding)) continue; + + if (!ShouldResourceBeConverted(descriptor_set_binding)) { + continue; + } + + if (variable_type->AsImage()) { + if (!descriptor_set_binding_pair_to_image + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } else if (variable_type->AsSampler()) { + if (!descriptor_set_binding_pair_to_sampler + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } + } + return true; +} + +Pass::Status ConvertToSampledImagePass::Process() { + Status status = Status::SuccessWithoutChange; + + DescriptorSetBindingToInstruction descriptor_set_binding_pair_to_sampler, + descriptor_set_binding_pair_to_image; + if (!CollectResourcesToConvert(&descriptor_set_binding_pair_to_sampler, + &descriptor_set_binding_pair_to_image)) { + return Status::Failure; + } + + for (auto& image : descriptor_set_binding_pair_to_image) { + status = CombineStatus( + status, UpdateImageVariableToSampledImage(image.second, image.first)); + if (status == Status::Failure) { + return status; + } + } + + for (const auto& sampler : descriptor_set_binding_pair_to_sampler) { + // Converting only a Sampler to Sampled Image is not allowed. It must have a + // corresponding image to combine the sampler with. + auto image_itr = descriptor_set_binding_pair_to_image.find(sampler.first); + if (image_itr == descriptor_set_binding_pair_to_image.end() || + image_itr->second == nullptr) { + return Status::Failure; + } + + status = CombineStatus( + status, CheckUsesOfSamplerVariable(sampler.second, image_itr->second)); + if (status == Status::Failure) { + return status; + } + } + + return status; +} + +void ConvertToSampledImagePass::FindUses(const Instruction* inst, + std::vector* uses, + uint32_t user_opcode) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(inst, [uses, user_opcode, this](Instruction* user) { + if (user->opcode() == user_opcode) { + uses->push_back(user); + } else if (user->opcode() == SpvOpCopyObject) { + FindUses(user, uses, user_opcode); + } + }); +} + +void ConvertToSampledImagePass::FindUsesOfImage( + const Instruction* image, std::vector* uses) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(image, [uses, this](Instruction* user) { + switch (user->opcode()) { + case SpvOpImageFetch: + case SpvOpImageRead: + case SpvOpImageWrite: + case SpvOpImageQueryFormat: + case SpvOpImageQueryOrder: + case SpvOpImageQuerySizeLod: + case SpvOpImageQuerySize: + case SpvOpImageQueryLevels: + case SpvOpImageQuerySamples: + case SpvOpImageSparseFetch: + uses->push_back(user); + default: + break; + } + if (user->opcode() == SpvOpCopyObject) { + FindUsesOfImage(user, uses); + } + }); +} + +Instruction* ConvertToSampledImagePass::CreateImageExtraction( + Instruction* sampled_image) { + InstructionBuilder builder( + context(), sampled_image->NextNode(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + return builder.AddUnaryOp( + GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image), + SpvOpImage, sampled_image->result_id()); +} + +uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage( + Instruction* image_variable) { + const auto* variable_type = GetVariableType(*image_variable); + if (variable_type == nullptr) return 0; + const auto* image_type = variable_type->AsImage(); + if (image_type == nullptr) return 0; + + analysis::Image image_type_for_sampled_image(*image_type); + analysis::SampledImage sampled_image_type(&image_type_for_sampled_image); + return context()->get_type_mgr()->GetTypeInstruction(&sampled_image_type); +} + +Instruction* ConvertToSampledImagePass::UpdateImageUses( + Instruction* sampled_image_load) { + std::vector uses_of_load; + FindUsesOfImage(sampled_image_load, &uses_of_load); + if (uses_of_load.empty()) return nullptr; + + auto* extracted_image = CreateImageExtraction(sampled_image_load); + for (auto* user : uses_of_load) { + user->SetInOperand(0, {extracted_image->result_id()}); + context()->get_def_use_mgr()->AnalyzeInstUse(user); + } + return extracted_image; +} + +bool ConvertToSampledImagePass:: + IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding) { + auto* def_use_mgr = context()->get_def_use_mgr(); + uint32_t sampler_id = sampled_image_inst->GetSingleWordInOperand(1u); + auto* sampler_load = def_use_mgr->GetDef(sampler_id); + if (sampler_load->opcode() != SpvOpLoad) return false; + auto* sampler = def_use_mgr->GetDef(sampler_load->GetSingleWordInOperand(0u)); + DescriptorSetAndBinding sampler_descriptor_set_binding; + return GetDescriptorSetBinding(*sampler, &sampler_descriptor_set_binding) && + sampler_descriptor_set_binding == descriptor_set_binding; +} + +void ConvertToSampledImagePass::UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding) { + std::vector sampled_image_users; + FindUses(image_load, &sampled_image_users, SpvOpSampledImage); + + auto* def_use_mgr = context()->get_def_use_mgr(); + for (auto* sampled_image_inst : sampled_image_users) { + if (IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + sampled_image_inst, image_descriptor_set_binding)) { + context()->ReplaceAllUsesWith(sampled_image_inst->result_id(), + image_load->result_id()); + def_use_mgr->AnalyzeInstUse(image_load); + context()->KillInst(sampled_image_inst); + } else { + if (!image_extraction) + image_extraction = CreateImageExtraction(image_load); + sampled_image_inst->SetInOperand(0, {image_extraction->result_id()}); + def_use_mgr->AnalyzeInstUse(sampled_image_inst); + } + } +} + +void ConvertToSampledImagePass::MoveInstructionNextToType(Instruction* inst, + uint32_t type_id) { + auto* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + inst->SetResultType(type_id); + inst->RemoveFromList(); + inst->InsertAfter(type_inst); +} + +bool ConvertToSampledImagePass::ConvertImageVariableToSampledImage( + Instruction* image_variable, uint32_t sampled_image_type_id) { + auto* sampled_image_type = + context()->get_type_mgr()->GetType(sampled_image_type_id); + if (sampled_image_type == nullptr) return false; + auto storage_class = GetStorageClass(*image_variable); + if (storage_class == SpvStorageClassMax) return false; + analysis::Pointer sampled_image_pointer(sampled_image_type, storage_class); + + // Make sure |image_variable| is behind its type i.e., avoid the forward + // reference. + uint32_t type_id = + context()->get_type_mgr()->GetTypeInstruction(&sampled_image_pointer); + MoveInstructionNextToType(image_variable, type_id); + return true; +} + +Pass::Status ConvertToSampledImagePass::UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding) { + std::vector image_variable_loads; + FindUses(image_variable, &image_variable_loads, SpvOpLoad); + if (image_variable_loads.empty()) return Status::SuccessWithoutChange; + + const uint32_t sampled_image_type_id = + GetSampledImageTypeForImage(image_variable); + if (!sampled_image_type_id) return Status::Failure; + + for (auto* load : image_variable_loads) { + load->SetResultType(sampled_image_type_id); + auto* image_extraction = UpdateImageUses(load); + UpdateSampledImageUses(load, image_extraction, descriptor_set_binding); + } + + return ConvertImageVariableToSampledImage(image_variable, + sampled_image_type_id) + ? Status::SuccessWithChange + : Status::Failure; +} + +bool ConvertToSampledImagePass::DoesSampledImageReferenceImage( + Instruction* sampled_image_inst, Instruction* image_variable) { + if (sampled_image_inst->opcode() != SpvOpSampledImage) return false; + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* image_load = GetNonCopyObjectDef( + def_use_mgr, sampled_image_inst->GetSingleWordInOperand(0u)); + if (image_load->opcode() != SpvOpLoad) return false; + auto* image = + GetNonCopyObjectDef(def_use_mgr, image_load->GetSingleWordInOperand(0u)); + return image->opcode() == SpvOpVariable && + image->result_id() == image_variable->result_id(); +} + +Pass::Status ConvertToSampledImagePass::CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with) { + if (image_to_be_combined_with == nullptr) return Status::Failure; + + std::vector sampler_variable_loads; + FindUses(sampler_variable, &sampler_variable_loads, SpvOpLoad); + for (auto* load : sampler_variable_loads) { + std::vector sampled_image_users; + FindUses(load, &sampled_image_users, SpvOpSampledImage); + for (auto* sampled_image_inst : sampled_image_users) { + if (!DoesSampledImageReferenceImage(sampled_image_inst, + image_to_be_combined_with)) { + return Status::Failure; + } + } + } + return Status::SuccessWithoutChange; +} + +std::unique_ptr +ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + const char* str) { + if (!str) return nullptr; + + auto descriptor_set_binding_pairs = + MakeUnique(); + + while (std::isspace(*str)) str++; // skip leading spaces. + + // The parsing loop, break when points to the end. + while (*str) { + // Parse the descriptor set. + uint32_t descriptor_set = 0; + str = ParseNumberUntilSeparator(str, &descriptor_set); + if (str == nullptr) return nullptr; + + // Find the ':', spaces between the descriptor set and the ':' are not + // allowed. + if (*str++ != ':') { + // ':' not found + return nullptr; + } + + // Parse the binding. + uint32_t binding = 0; + str = ParseNumberUntilSeparator(str, &binding); + if (str == nullptr) return nullptr; + + descriptor_set_binding_pairs->push_back({descriptor_set, binding}); + + // Skip trailing spaces. + while (std::isspace(*str)) str++; + } + + return descriptor_set_binding_pairs; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/convert_to_sampled_image_pass.h b/source/opt/convert_to_sampled_image_pass.h new file mode 100644 index 00000000..d3938af7 --- /dev/null +++ b/source/opt/convert_to_sampled_image_pass.h @@ -0,0 +1,207 @@ +// Copyright (c) 2021 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_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ +#define SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ + +#include +#include +#include + +#include "source/opt/pass.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +// A struct for a pair of descriptor set and binding. +struct DescriptorSetAndBinding { + uint32_t descriptor_set; + uint32_t binding; + + bool operator==(const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding.descriptor_set == descriptor_set && + descriptor_set_binding.binding == binding; + } +}; + +// See optimizer.hpp for documentation. +class ConvertToSampledImagePass : public Pass { + public: + // Hashing functor for the pair of descriptor set and binding. + struct DescriptorSetAndBindingHash { + size_t operator()( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return std::hash()(descriptor_set_binding.descriptor_set) ^ + std::hash()(descriptor_set_binding.binding); + } + }; + + using SetOfDescriptorSetAndBindingPairs = + std::unordered_set; + using DescriptorSetBindingToInstruction = + std::unordered_map; + + explicit ConvertToSampledImagePass( + const std::vector& descriptor_set_binding_pairs) + : descriptor_set_binding_pairs_(descriptor_set_binding_pairs.begin(), + descriptor_set_binding_pairs.end()) {} + + const char* name() const override { return "convert-to-sampled-image"; } + Status Process() override; + + // Parses the given null-terminated C string to get a vector of descriptor set + // and binding pairs. Returns a unique pointer to the vector of descriptor set + // and binding pairs built from the given |str| on success. Returns a nullptr + // if the given string is not valid for building the vector of pairs. + // A valid string for building the vector of pairs should follow the rule + // below: + // + // ": : ..." + // Example: + // "3:5 2:1 0:4" + // + // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t', + // '\f', '\v'). Each entry corresponds to a descriptor set and binding pair. + // Multiple spaces between, before or after entries are allowed. However, + // spaces are not allowed within a descriptor set or binding. + // + // In each entry, the descriptor set and binding are separated by ':'. + // Missing ':' in any entry is invalid. And it is invalid to have blank + // spaces in between the descriptor set and ':' or ':' and the binding. + // + // : the descriptor set. + // The text must represent a valid uint32_t number. + // + // : the binding. + // The text must represent a valid uint32_t number. + static std::unique_ptr> + ParseDescriptorSetBindingPairsString(const char* str); + + private: + // Collects resources to convert to sampled image and saves them in + // |descriptor_set_binding_pair_to_sampler| if the resource is a sampler and + // saves them in |descriptor_set_binding_pair_to_image| if the resource is an + // image. Returns false if two samplers or two images have the same descriptor + // set and binding. Otherwise, returns true. + bool CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const; + + // Finds an OpDecorate with DescriptorSet decorating |inst| and another + // OpDecorate with Binding decorating |inst|. Stores the descriptor set and + // binding in |descriptor_set_binding|. Returns whether it successfully finds + // the descriptor set and binding or not. + bool GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const; + + // Returns whether |descriptor_set_binding| is a pair of a descriptor set + // and a binding that we have to convert resources with it to a sampled image + // or not. + bool ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const; + + // Returns the pointee type of the type of variable |variable|. If |variable| + // is not an OpVariable instruction, just returns nullptr. + const analysis::Type* GetVariableType(const Instruction& variable) const; + + // Returns the storage class of |variable|. + SpvStorageClass GetStorageClass(const Instruction& variable) const; + + // Finds |inst|'s users whose opcode is |user_opcode| or users of OpCopyObject + // instructions of |inst| whose opcode is |user_opcode| and puts them in + // |uses|. + void FindUses(const Instruction* inst, std::vector* uses, + uint32_t user_opcode) const; + + // Finds OpImage* instructions using |image| or OpCopyObject instructions that + // copy |image| and puts them in |uses|. + void FindUsesOfImage(const Instruction* image, + std::vector* uses) const; + + // Creates an OpImage instruction that extracts the image from the sampled + // image |sampled_image|. + Instruction* CreateImageExtraction(Instruction* sampled_image); + + // Converts |image_variable| whose type is an image pointer to sampled image + // type. Updates users of |image_variable| accordingly. If some instructions + // e.g., OpImageRead use |image_variable| as an Image operand, creates an + // image extracted from the sampled image using OpImage and replace the Image + // operands of the users with the extracted image. If some OpSampledImage + // instructions use |image_variable| and sampler whose descriptor set and + // binding are the same with |image_variable|, just combines |image_variable| + // and the sampler to a sampled image. + Pass::Status UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Returns the id of type sampled image type whose image type is the one of + // |image_variable|. + uint32_t GetSampledImageTypeForImage(Instruction* image_variable); + + // Moves |inst| next to the OpType* instruction with |type_id|. + void MoveInstructionNextToType(Instruction* inst, uint32_t type_id); + + // Converts |image_variable| whose type is an image pointer to sampled image + // with the type id |sampled_image_type_id|. Returns whether it successfully + // converts the type of |image_variable| or not. + bool ConvertImageVariableToSampledImage(Instruction* image_variable, + uint32_t sampled_image_type_id); + + // Replaces |sampled_image_load| instruction used by OpImage* with the image + // extracted from |sampled_image_load|. Returns the extracted image or nullptr + // if it does not have uses. + Instruction* UpdateImageUses(Instruction* sampled_image_load); + + // Returns true if the sampler of |sampled_image_inst| is decorated by a + // descriptor set and a binding |descriptor_set_binding|. + bool IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Replaces OpSampledImage instructions using |image_load| with |image_load| + // if the sampler of the OpSampledImage instruction has descriptor set and + // binding |image_descriptor_set_binding|. Otherwise, replaces |image_load| + // with |image_extraction|. + void UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding); + + // Checks the uses of |sampler_variable|. When a sampler is used by + // OpSampledImage instruction, the corresponding image must be + // |image_to_be_combined_with| that should be already converted to a sampled + // image by UpdateImageVariableToSampledImage() method. + Pass::Status CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with); + + // Returns true if Image operand of |sampled_image_inst| is the image of + // |image_variable|. + bool DoesSampledImageReferenceImage(Instruction* sampled_image_inst, + Instruction* image_variable); + + // A set of pairs of descriptor set and binding. If an image and/or a sampler + // have a pair of descriptor set and binding that is an element of + // |descriptor_set_binding_pairs_|, they/it will be converted to a sampled + // image by this pass. + const SetOfDescriptorSetAndBindingPairs descriptor_set_binding_pairs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 69103b26..858c95ee 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -499,6 +499,26 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateAmdExtToKhrPass()); } else if (pass_name == "interpolate-fixup") { RegisterPass(CreateInterpolateFixupPass()); + } else if (pass_name == "convert-to-sampled-image") { + if (pass_args.size() > 0) { + auto descriptor_set_binding_pairs = + opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + pass_args.c_str()); + if (!descriptor_set_binding_pairs) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --convert-to-sampled-image: %s", + pass_args.c_str()); + return false; + } + RegisterPass(CreateConvertToSampledImagePass( + std::move(*descriptor_set_binding_pairs))); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid pairs of descriptor set and binding '%s'. Expected a " + "string of : pairs.", + pass_args.c_str()); + return false; + } } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -940,4 +960,11 @@ Optimizer::PassToken CreateInterpolateFixupPass() { MakeUnique()); } +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs) { + return MakeUnique( + MakeUnique(descriptor_set_binding_pairs)); +} + } // namespace spvtools diff --git a/source/opt/passes.h b/source/opt/passes.h index aeae4449..da837f2a 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -26,6 +26,7 @@ #include "source/opt/combine_access_chains.h" #include "source/opt/compact_ids_pass.h" #include "source/opt/convert_to_half_pass.h" +#include "source/opt/convert_to_sampled_image_pass.h" #include "source/opt/copy_prop_arrays.h" #include "source/opt/dead_branch_elim_pass.h" #include "source/opt/dead_insert_elim_pass.h" diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 76ca99eb..11299755 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -30,6 +30,7 @@ add_spvtools_unittest(TARGET opt constant_manager_test.cpp control_dependence.cpp convert_relaxed_to_half_test.cpp + convert_to_sampled_image_test.cpp copy_prop_array_test.cpp dataflow.cpp dead_branch_elim_test.cpp diff --git a/test/opt/convert_to_sampled_image_test.cpp b/test/opt/convert_to_sampled_image_test.cpp new file mode 100644 index 00000000..37f65601 --- /dev/null +++ b/test/opt/convert_to_sampled_image_test.cpp @@ -0,0 +1,353 @@ +// Copyright (c) 2021 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 + +#include "gmock/gmock.h" +#include "source/opt/convert_to_sampled_image_pass.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using testing::Eq; +using VectorOfDescriptorSetAndBindingPairs = + std::vector; + +struct DescriptorSetAndBindingStringParsingTestCase { + const char* descriptor_set_binding_str; + bool expect_success; + VectorOfDescriptorSetAndBindingPairs expected_descriptor_set_binding_pairs; +}; + +using DescriptorSetAndBindingStringParsingTest = + ::testing::TestWithParam; + +TEST_P(DescriptorSetAndBindingStringParsingTest, TestCase) { + const auto& tc = GetParam(); + auto actual_descriptor_set_binding_pairs = + ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + tc.descriptor_set_binding_str); + if (tc.expect_success) { + EXPECT_NE(nullptr, actual_descriptor_set_binding_pairs); + if (actual_descriptor_set_binding_pairs) { + EXPECT_THAT(*actual_descriptor_set_binding_pairs, + Eq(tc.expected_descriptor_set_binding_pairs)); + } + } else { + EXPECT_EQ(nullptr, actual_descriptor_set_binding_pairs); + } +} + +INSTANTIATE_TEST_SUITE_P( + ValidString, DescriptorSetAndBindingStringParsingTest, + ::testing::ValuesIn(std::vector< + DescriptorSetAndBindingStringParsingTestCase>{ + // 0. empty vector + {"", true, VectorOfDescriptorSetAndBindingPairs({})}, + // 1. one pair + {"100:1024", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 2. two pairs + {"100:1024 200:2048", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{100, 1024}, + DescriptorSetAndBinding{200, 2048}})}, + // 3. spaces between entries + {"100:1024 \n \r \t \v \f 200:2048", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{100, 1024}, + DescriptorSetAndBinding{200, 2048}})}, + // 4. \t, \n, \r and spaces before spec id + {" \n \r\t \t \v \f 100:1024", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 5. \t, \n, \r and spaces after value string + {"100:1024 \n \r\t \t \v \f ", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 6. maximum spec id + {"4294967295:0", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{ + 4294967295, 0}})}, + // 7. minimum spec id + {"0:100", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0, + 100}})}, + // 8. multiple entries + {"101:1 102:2 103:3 104:4 200:201 9999:1000", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{101, 1}, DescriptorSetAndBinding{102, 2}, + DescriptorSetAndBinding{103, 3}, DescriptorSetAndBinding{104, 4}, + DescriptorSetAndBinding{200, 201}, + DescriptorSetAndBinding{9999, 1000}})}, + })); + +INSTANTIATE_TEST_SUITE_P( + InvalidString, DescriptorSetAndBindingStringParsingTest, + ::testing::ValuesIn( + std::vector{ + // 0. missing default value + {"100:", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 1. descriptor set is not an integer + {"100.0:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 2. descriptor set is not a number + {"something_not_a_number:1", false, + VectorOfDescriptorSetAndBindingPairs{}}, + // 3. only descriptor set number + {"100", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 4. empty descriptor set + {":3", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 5. only colon + {":", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 6. descriptor set overflow + {"4294967296:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 7. descriptor set less than 0 + {"-1:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 8. nullptr + {nullptr, false, VectorOfDescriptorSetAndBindingPairs{}}, + // 9. only a number is invalid + {"1234", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 10. invalid entry separator + {"12:34;23:14", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 11. invalid descriptor set and default value separator + {"12@34", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 12. spaces before colon + {"100 :1024", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 13. spaces after colon + {"100: 1024", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 14. descriptor set represented in hex float format is invalid + {"0x3p10:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + })); + +std::string BuildShader(const char* shader_decorate_instructions, + const char* shader_image_and_sampler_variables, + const char* shader_body) { + // Base HLSL code: + // + // SamplerState sam : register(s2); + // Texture2D texture : register(t5); + // + // float4 main() : SV_TARGET { + // return texture.SampleLevel(sam, float2(1, 2), 10, 2); + // } + std::stringstream ss; + ss << R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_TARGET + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %type_2d_image "type.2d.image" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %main "main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %out_var_SV_TARGET Location 0 + )"; + ss << shader_decorate_instructions; + ss << R"( + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %v2float = OpTypeVector %float 2 + %12 = OpConstantComposite %v2float %float_1 %float_2 + %float_10 = OpConstant %float 10 + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %v2int = OpTypeVector %int 2 + %17 = OpConstantComposite %v2int %int_2 %int_2 +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + )"; + ss << shader_image_and_sampler_variables; + ss << R"( +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %23 + %24 = OpLabel + )"; + ss << shader_body; + ss << R"( + OpReturn + OpFunctionEnd + )"; + return ss.str(); +} + +using ConvertToSampledImageTest = PassTest<::testing::Test>; + +TEST_F(ConvertToSampledImageTest, Texture2DAndSamplerToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 5 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + ; CHECK-NOT: OpVariable %_ptr_UniformConstant_type_2d_image + + ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]] + ; CHECK: OpImageSampleExplicitLod %v4float [[load]] + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunAndMatch( + shader, /* do_validate = */ true, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(ConvertToSampledImageTest, Texture2DToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]] + ; CHECK: [[image_extraction:%\w+]] = OpImage %type_2d_image [[load]] + ; CHECK: OpSampledImage %type_sampled_image [[image_extraction]] + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunAndMatch( + shader, /* do_validate = */ true, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(ConvertToSampledImageTest, SamplerToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +TEST_F(ConvertToSampledImageTest, TwoImagesWithDuplicatedDescriptorSetBinding) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture0 DescriptorSet 0 + OpDecorate %texture0 Binding 5 + OpDecorate %texture1 DescriptorSet 0 + OpDecorate %texture1 Binding 5 + )", + R"( + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %texture1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture0 + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +TEST_F(ConvertToSampledImageTest, + TwoSamplersWithDuplicatedDescriptorSetBinding) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam0 DescriptorSet 0 + OpDecorate %sam0 Binding 2 + OpDecorate %sam1 DescriptorSet 0 + OpDecorate %sam1 Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + %sam0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %sam1 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam0 + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 53bf31d6..28770cb9 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -143,6 +143,15 @@ Options (in lexicographical order):)", does not support RelaxedPrecision or ignores it. This pass also removes all RelaxedPrecision decorations.)"); printf(R"( + --convert-to-sampled-image ": ..." + convert images and/or samplers with the given pairs of descriptor + set and binding to sampled images. If a pair of an image and a + sampler have the same pair of descriptor set and binding that is + one of the given pairs, they will be converted to a sampled + image. In addition, if only an image or a sampler has the + descriptor set and binding that is one of the given pairs, it + will be converted to a sampled image.)"); + printf(R"( --copy-propagate-arrays Does propagation of memory references when an array is a copy of another. It will only propagate an array if the source is never -- cgit v1.2.3 From 926ff6d1bab0f6982838ac8ec212151563b1a42d Mon Sep 17 00:00:00 2001 From: Jamie Madill Date: Wed, 18 Aug 2021 08:53:21 -0400 Subject: GN: Suppress unreachable code warnings. (#4476) These are popping up in a Chromium test integration. Fixes #4475 --- BUILD.gn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BUILD.gn b/BUILD.gn index 8a0e5014..20fdeedf 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -378,6 +378,8 @@ config("spvtools_internal_config") { cflags += [ "-Wno-implicit-fallthrough", "-Wno-newline-eof", + "-Wno-unreachable-code-break", + "-Wno-unreachable-code-return", ] } else if (!is_win) { # Work around a false-positive on a Skia GCC 10 builder. -- cgit v1.2.3 From e172833bd165599672e07f0a0199dcb5951cbb72 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 18 Aug 2021 13:01:37 -0400 Subject: Don't double count variables for location validation (#4474) Fixes #4403 * Pre-1.4 modules could have duplicate interface entries * Don't double count variables for locations --- source/val/validate_interfaces.cpp | 6 ++++++ test/val/val_interfaces_test.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index 298e4e2f..d3ef5386 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -468,6 +468,7 @@ spv_result_t ValidateLocations(ValidationState_t& _, std::unordered_set input_locations; std::unordered_set output_locations_index0; std::unordered_set output_locations_index1; + std::unordered_set seen; for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { auto interface_id = entry_point->GetOperandAs(i); auto interface_var = _.FindDef(interface_id); @@ -476,6 +477,11 @@ spv_result_t ValidateLocations(ValidationState_t& _, storage_class != SpvStorageClassOutput) { continue; } + if (!seen.insert(interface_id).second) { + // Pre-1.4 an interface variable could be listed multiple times in an + // entry point. Validation for 1.4 or later is done elsewhere. + continue; + } auto locations = (storage_class == SpvStorageClassInput) ? &input_locations diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index 6869e794..be13cd71 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -1410,6 +1410,31 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateInterfacesTest, DuplicateInterfaceVariableSuccess) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in %out %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%in_ptr = OpTypePointer Input %float +%out_ptr = OpTypePointer Output %float +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From a9ce5e07c627fb846f7cf989e395b49f170e18d0 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 18 Aug 2021 13:03:45 -0400 Subject: Fix matrix stride validation (#4468) Fixes #4454 * Use MatrixStride from member decorations instead of from matrix decorations --- source/val/validate_decorations.cpp | 10 +-- test/val/val_decoration_test.cpp | 143 ++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 6 deletions(-) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 615adc72..c483635b 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -506,12 +506,10 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, return recursive_status; // Check matrix stride. if (SpvOpTypeMatrix == opcode) { - for (auto& decoration : vstate.id_decorations(id)) { - if (SpvDecorationMatrixStride == decoration.dec_type() && - !IsAlignedTo(decoration.params()[0], alignment)) - return fail(memberIdx) - << "is a matrix with stride " << decoration.params()[0] - << " not satisfying alignment to " << alignment; + const auto stride = constraint.matrix_stride; + if (!IsAlignedTo(stride, alignment)) { + return fail(memberIdx) << "is a matrix with stride " << stride + << " not satisfying alignment to " << alignment; } } diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index 096fd172..b9a413e3 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -7648,6 +7648,149 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) { "member 0 at offset 1 is not aligned to 4")); } +TEST_F(ValidateDecorations, BadMatrixStrideUniform) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer Uniform %block +%var = OpVariable %block_ptr Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 0 is " + "a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStrideStorageBuffer) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer StorageBuffer %block +%var = OpVariable %block_ptr StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in StorageBuffer " + "storage class must follow standard storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStridePushConstant) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer PushConstant %block +%var = OpVariable %block_ptr PushConstant +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in PushConstant " + "storage class must follow standard storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStrideStorageBufferScalarLayout) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer StorageBuffer %block +%var = OpVariable %block_ptr StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->scalar_block_layout = true; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in StorageBuffer " + "storage class must follow scalar storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 4")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 1ad8b71359b27835c9682dbcf6ef152fe14cd473 Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Wed, 18 Aug 2021 15:17:03 -0500 Subject: spirv-lint: add basic CLI argument handling (#4478) Mostly copied from spirv-opt. Simply reads in a single input file. --- BUILD.bazel | 1 + tools/lint/lint.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 816f9328..4af0ce5d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -347,6 +347,7 @@ cc_binary( cc_binary( name = "spirv-lint", srcs = [ + "tools/io.h", "tools/lint/lint.cpp", ], copts = COMMON_COPTS, diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp index d4c8bde4..5c2a82ac 100644 --- a/tools/lint/lint.cpp +++ b/tools/lint/lint.cpp @@ -14,21 +14,62 @@ #include +#include "source/opt/log.h" #include "spirv-tools/linter.hpp" +#include "tools/io.h" #include "tools/util/cli_consumer.h" const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +namespace { +// Status and actions to perform after parsing command-line arguments. +enum LintActions { LINT_CONTINUE, LINT_STOP }; + +struct LintStatus { + LintActions action; + int code; +}; + +// Parses command-line flags. |argc| contains the number of command-line flags. +// |argv| points to an array of strings holding the flags. +// +// On return, this function stores the name of the input program in |in_file|. +// The return value indicates whether optimization should continue and a status +// code indicating an error or success. +LintStatus ParseFlags(int argc, const char** argv, const char** in_file) { + // TODO (dongja): actually parse flags, etc. + if (argc != 2) { + spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {}, + "expected exactly one argument: in_file"); + return {LINT_STOP, 1}; + } + + *in_file = argv[1]; + + return {LINT_CONTINUE, 0}; +} +} // namespace + int main(int argc, const char** argv) { - (void)argc; - (void)argv; + const char* in_file = nullptr; spv_target_env target_env = kDefaultEnvironment; spvtools::Linter linter(target_env); linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); - bool ok = linter.Run(nullptr, 0); + LintStatus status = ParseFlags(argc, argv, &in_file); + + if (status.action == LINT_STOP) { + return status.code; + } + + std::vector binary; + if (!ReadBinaryFile(in_file, &binary)) { + return 1; + } + + bool ok = linter.Run(binary.data(), binary.size()); return ok ? 0 : 1; } -- cgit v1.2.3 From d699296b4d4e3aa4c5b12077264b73a33c06c69a Mon Sep 17 00:00:00 2001 From: sfricke-samsung <46493288+sfricke-samsung@users.noreply.github.com> Date: Thu, 19 Aug 2021 13:06:01 -0700 Subject: spirv-val: Fix WorkgroupSize VUID 04425 (#4482) --- source/val/validate_builtins.cpp | 10 +++++---- test/val/val_builtins_test.cpp | 45 ++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index 9c737ec0..a6e624f7 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp @@ -2846,7 +2846,7 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, or TaskNV execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -2928,7 +2928,7 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, or TaskNV execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -3070,14 +3070,16 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference( const Instruction& referenced_from_inst) { if (spvIsVulkanEnv(_.context()->target_env)) { for (const SpvExecutionModel execution_model : execution_models_) { - if (execution_model != SpvExecutionModelGLCompute) { + if (execution_model != SpvExecutionModelGLCompute && + execution_model != SpvExecutionModelTaskNV && + execution_model != SpvExecutionModelMeshNV) { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) << _.VkErrorID(4425) << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, or TaskNV execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index 2116fff2..84952645 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -767,18 +767,18 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotGLCompute, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", - "WorkgroupId"), - Values("Vertex", "Fragment", "Geometry", "TessellationControl", - "TessellationEvaluation"), - Values("Input"), Values("%u32vec3"), - Values("VUID-GlobalInvocationId-GlobalInvocationId-04236 " - "VUID-LocalInvocationId-LocalInvocationId-04281 " - "VUID-NumWorkgroups-NumWorkgroups-04296 " - "VUID-WorkgroupId-WorkgroupId-04422"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with GLCompute execution model")))); + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("Vertex", "Fragment", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32vec3"), + Values("VUID-GlobalInvocationId-GlobalInvocationId-04236 " + "VUID-LocalInvocationId-LocalInvocationId-04281 " + "VUID-NumWorkgroups-NumWorkgroups-04296 " + "VUID-WorkgroupId-WorkgroupId-04422"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute, MeshNV, or " + "TaskNV execution model")))); INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotInput, @@ -2828,9 +2828,10 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeFragment) { CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used " - "only with GLCompute execution model")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used " + "only with GLCompute, MeshNV, or TaskNV execution model")); EXPECT_THAT(getDiagnosticString(), HasSubstr("is referencing ID <2> (OpConstantComposite) which is " "decorated with BuiltIn WorkgroupSize in function <1> " @@ -3711,13 +3712,13 @@ OpFunctionEnd INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotCompute, ValidateVulkanSubgroupBuiltIns, - Combine( - Values("SubgroupId", "NumSubgroups"), Values("Vertex"), Values("Input"), - Values("%u32"), - Values("VUID-SubgroupId-SubgroupId-04367 " - "VUID-NumSubgroups-NumSubgroups-04293"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with GLCompute execution model")))); + Combine(Values("SubgroupId", "NumSubgroups"), Values("Vertex"), + Values("Input"), Values("%u32"), + Values("VUID-SubgroupId-SubgroupId-04367 " + "VUID-NumSubgroups-NumSubgroups-04293"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute, MeshNV, or " + "TaskNV execution model")))); INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotU32, ValidateVulkanSubgroupBuiltIns, -- cgit v1.2.3 From 937227c761a9c1b5b0ba4b8c3eeb13b8b19a01a2 Mon Sep 17 00:00:00 2001 From: dong-ja <52670911+dong-ja@users.noreply.github.com> Date: Mon, 23 Aug 2021 16:03:28 -0500 Subject: Add divergence analysis to linter (#4465) Currently, handles promotion of divergence due to reconvergence rules, but doesn't handle "late merges" caused by a later-than-necessary declared merge block. Co-authored-by: Jakub Kuderski --- BUILD.bazel | 2 +- source/lint/CMakeLists.txt | 3 + source/lint/divergence_analysis.cpp | 245 ++++++++++++ source/lint/divergence_analysis.h | 163 ++++++++ test/lint/CMakeLists.txt | 2 +- test/lint/divergence_analysis_test.cpp | 700 +++++++++++++++++++++++++++++++++ test/lint/placeholder_test.cpp | 25 -- 7 files changed, 1113 insertions(+), 27 deletions(-) create mode 100644 source/lint/divergence_analysis.cpp create mode 100644 source/lint/divergence_analysis.h create mode 100644 test/lint/divergence_analysis_test.cpp delete mode 100644 test/lint/placeholder_test.cpp diff --git a/BUILD.bazel b/BUILD.bazel index 4af0ce5d..68e612ac 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -236,7 +236,7 @@ cc_library( cc_library( name = "spirv_tools_lint", - srcs = glob(["source/lint/*.cpp"]), + srcs = glob(["source/lint/*.cpp", "source/lint/*.h"]), hdrs = ["include/spirv-tools/linter.hpp"], copts = COMMON_COPTS, linkstatic = 1, diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt index d996e3c9..f9cae28a 100644 --- a/source/lint/CMakeLists.txt +++ b/source/lint/CMakeLists.txt @@ -12,7 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. set(SPIRV_TOOLS_LINT_SOURCES + divergence_analysis.h + linter.cpp + divergence_analysis.cpp ) if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) diff --git a/source/lint/divergence_analysis.cpp b/source/lint/divergence_analysis.cpp new file mode 100644 index 00000000..b5a72b45 --- /dev/null +++ b/source/lint/divergence_analysis.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2021 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/lint/divergence_analysis.h" + +#include "source/opt/basic_block.h" +#include "source/opt/control_dependence.h" +#include "source/opt/dataflow.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace lint { + +void DivergenceAnalysis::EnqueueSuccessors(opt::Instruction* inst) { + // Enqueue control dependents of block, if applicable. + // There are two ways for a dependence source to be updated: + // 1. control -> control: source block is marked divergent. + // 2. data -> control: branch condition is marked divergent. + uint32_t block_id; + if (inst->IsBlockTerminator()) { + block_id = context().get_instr_block(inst)->id(); + } else if (inst->opcode() == SpvOpLabel) { + block_id = inst->result_id(); + opt::BasicBlock* bb = context().cfg()->block(block_id); + // Only enqueue phi instructions, as other uses don't affect divergence. + bb->ForEachPhiInst([this](opt::Instruction* phi) { Enqueue(phi); }); + } else { + opt::ForwardDataFlowAnalysis::EnqueueUsers(inst); + return; + } + if (!cd_.HasBlock(block_id)) { + return; + } + for (const spvtools::opt::ControlDependence& dep : + cd_.GetDependenceTargets(block_id)) { + opt::Instruction* target_inst = + context().cfg()->block(dep.target_bb_id())->GetLabelInst(); + Enqueue(target_inst); + } +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::Visit( + opt::Instruction* inst) { + if (inst->opcode() == SpvOpLabel) { + return VisitBlock(inst->result_id()); + } else { + return VisitInstruction(inst); + } +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitBlock(uint32_t id) { + if (!cd_.HasBlock(id)) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel& cur_level = divergence_[id]; + if (cur_level == DivergenceLevel::kDivergent) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel orig = cur_level; + for (const spvtools::opt::ControlDependence& dep : + cd_.GetDependenceSources(id)) { + if (divergence_[dep.source_bb_id()] > cur_level) { + cur_level = divergence_[dep.source_bb_id()]; + divergence_source_[id] = dep.source_bb_id(); + } else if (dep.source_bb_id() != 0) { + uint32_t condition_id = dep.GetConditionID(*context().cfg()); + DivergenceLevel dep_level = divergence_[condition_id]; + // Check if we are along the chain of unconditional branches starting from + // the branch target. + if (follow_unconditional_branches_[dep.branch_target_bb_id()] != + follow_unconditional_branches_[dep.target_bb_id()]) { + // We must have reconverged in order to reach this block. + // Promote partially uniform to divergent. + if (dep_level == DivergenceLevel::kPartiallyUniform) { + dep_level = DivergenceLevel::kDivergent; + } + } + if (dep_level > cur_level) { + cur_level = dep_level; + divergence_source_[id] = condition_id; + divergence_dependence_source_[id] = dep.source_bb_id(); + } + } + } + return cur_level > orig ? VisitResult::kResultChanged + : VisitResult::kResultFixed; +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitInstruction( + opt::Instruction* inst) { + if (inst->IsBlockTerminator()) { + // This is called only when the condition has changed, so return changed. + return VisitResult::kResultChanged; + } + if (!inst->HasResultId()) { + return VisitResult::kResultFixed; + } + uint32_t id = inst->result_id(); + DivergenceLevel& cur_level = divergence_[id]; + if (cur_level == DivergenceLevel::kDivergent) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel orig = cur_level; + cur_level = ComputeInstructionDivergence(inst); + return cur_level > orig ? VisitResult::kResultChanged + : VisitResult::kResultFixed; +} + +DivergenceAnalysis::DivergenceLevel +DivergenceAnalysis::ComputeInstructionDivergence(opt::Instruction* inst) { + // TODO(kuhar): Check to see if inst is decorated with Uniform or UniformId + // and use that to short circuit other checks. Uniform is for subgroups which + // would satisfy derivative groups too. UniformId takes a scope, so if it is + // subgroup or greater it could satisfy derivative group and + // Device/QueueFamily could satisfy fully uniform. + uint32_t id = inst->result_id(); + // Handle divergence roots. + if (inst->opcode() == SpvOpFunctionParameter) { + divergence_source_[id] = 0; + return divergence_[id] = DivergenceLevel::kDivergent; + } else if (inst->IsLoad()) { + spvtools::opt::Instruction* var = inst->GetBaseAddress(); + if (var->opcode() != SpvOpVariable) { + // Assume divergent. + divergence_source_[id] = 0; + return DivergenceLevel::kDivergent; + } + DivergenceLevel ret = ComputeVariableDivergence(var); + if (ret > DivergenceLevel::kUniform) { + divergence_source_[inst->result_id()] = 0; + } + return divergence_[id] = ret; + } + // Get the maximum divergence of the operands. + DivergenceLevel ret = DivergenceLevel::kUniform; + inst->ForEachInId([this, inst, &ret](const uint32_t* op) { + if (!op) return; + if (divergence_[*op] > ret) { + divergence_source_[inst->result_id()] = *op; + ret = divergence_[*op]; + } + }); + divergence_[inst->result_id()] = ret; + return ret; +} + +DivergenceAnalysis::DivergenceLevel +DivergenceAnalysis::ComputeVariableDivergence(opt::Instruction* var) { + uint32_t type_id = var->type_id(); + spvtools::opt::analysis::Pointer* type = + context().get_type_mgr()->GetType(type_id)->AsPointer(); + assert(type != nullptr); + uint32_t def_id = var->result_id(); + DivergenceLevel ret; + switch (type->storage_class()) { + case SpvStorageClassFunction: + case SpvStorageClassGeneric: + case SpvStorageClassAtomicCounter: + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassOutput: + case SpvStorageClassWorkgroup: + case SpvStorageClassImage: // Image atomics probably aren't uniform. + case SpvStorageClassPrivate: + ret = DivergenceLevel::kDivergent; + break; + case SpvStorageClassInput: + ret = DivergenceLevel::kDivergent; + // If this variable has a Flat decoration, it is partially uniform. + // TODO(kuhar): Track access chain indices and also consider Flat members + // of a structure. + context().get_decoration_mgr()->WhileEachDecoration( + def_id, SpvDecorationFlat, [&ret](const opt::Instruction&) { + ret = DivergenceLevel::kPartiallyUniform; + return false; + }); + break; + case SpvStorageClassUniformConstant: + // May be a storage image which is also written to; mark those as + // divergent. + if (!var->IsVulkanStorageImage() || var->IsReadOnlyPointer()) { + ret = DivergenceLevel::kUniform; + } else { + ret = DivergenceLevel::kDivergent; + } + break; + case SpvStorageClassUniform: + case SpvStorageClassPushConstant: + case SpvStorageClassCrossWorkgroup: // Not for shaders; default uniform. + default: + ret = DivergenceLevel::kUniform; + break; + } + return ret; +} + +void DivergenceAnalysis::Setup(opt::Function* function) { + // TODO(kuhar): Run functions called by |function| so we can detect + // reconvergence caused by multiple returns. + cd_.ComputeControlDependenceGraph( + *context().cfg(), *context().GetPostDominatorAnalysis(function)); + context().cfg()->ForEachBlockInPostOrder( + function->entry().get(), [this](const opt::BasicBlock* bb) { + uint32_t id = bb->id(); + if (bb->terminator() == nullptr || + bb->terminator()->opcode() != SpvOpBranch) { + follow_unconditional_branches_[id] = id; + } else { + uint32_t target_id = bb->terminator()->GetSingleWordInOperand(0); + // Target is guaranteed to have been visited before us in postorder. + follow_unconditional_branches_[id] = + follow_unconditional_branches_[target_id]; + } + }); +} + +std::ostream& operator<<(std::ostream& os, + DivergenceAnalysis::DivergenceLevel level) { + switch (level) { + case DivergenceAnalysis::DivergenceLevel::kUniform: + return os << "uniform"; + case DivergenceAnalysis::DivergenceLevel::kPartiallyUniform: + return os << "partially uniform"; + case DivergenceAnalysis::DivergenceLevel::kDivergent: + return os << "divergent"; + default: + return os << ""; + } +} + +} // namespace lint +} // namespace spvtools diff --git a/source/lint/divergence_analysis.h b/source/lint/divergence_analysis.h new file mode 100644 index 00000000..4d595ec2 --- /dev/null +++ b/source/lint/divergence_analysis.h @@ -0,0 +1,163 @@ +// Copyright (c) 2021 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_LINT_DIVERGENCE_ANALYSIS_H_ +#define SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ + +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/control_dependence.h" +#include "source/opt/dataflow.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace lint { + +// Computes the static divergence level for blocks (control flow) and values. +// +// A value is uniform if all threads that execute it are guaranteed to have the +// same value. Similarly, a value is partially uniform if this is true only +// within each derivative group. If neither apply, it is divergent. +// +// Control flow through a block is uniform if for any possible execution and +// point in time, all threads are executing it, or no threads are executing it. +// In particular, it is never possible for some threads to be inside the block +// and some threads not executing. +// TODO(kuhar): Clarify the difference between uniform, divergent, and +// partially-uniform execution in this analysis. +// +// Caveat: +// As we use control dependence to determine how divergence is propagated, this +// analysis can be overly permissive when the merge block for a conditional +// branch or switch is later than (strictly postdominates) the expected merge +// block, which is the immediate postdominator. However, this is not expected to +// be a problem in practice, given that SPIR-V is generally output by compilers +// and other automated tools, which would assign the earliest possible merge +// block, rather than written by hand. +// TODO(kuhar): Handle late merges. +class DivergenceAnalysis : public opt::ForwardDataFlowAnalysis { + public: + // The tightest (most uniform) level of divergence that can be determined + // statically for a value or control flow for a block. + // + // The values are ordered such that A > B means that A is potentially more + // divergent than B. + // TODO(kuhar): Rename |PartiallyUniform' to something less confusing. For + // example, the enum could be based on scopes. + enum class DivergenceLevel { + // The value or control flow is uniform across the entire invocation group. + kUniform = 0, + // The value or control flow is uniform across the derivative group, but not + // the invocation group. + kPartiallyUniform = 1, + // The value or control flow is not statically uniform. + kDivergent = 2, + }; + + DivergenceAnalysis(opt::IRContext& context) + : ForwardDataFlowAnalysis(context, LabelPosition::kLabelsAtEnd) {} + + // Returns the divergence level for the given value (non-label instructions), + // or control flow for the given block. + DivergenceLevel GetDivergenceLevel(uint32_t id) { + auto it = divergence_.find(id); + if (it == divergence_.end()) { + return DivergenceLevel::kUniform; + } + return it->second; + } + + // Returns the divergence source for the given id. The following types of + // divergence flows from A to B are possible: + // + // data -> data: A is used as an operand in the definition of B. + // data -> control: B is control-dependent on a branch with condition A. + // control -> data: B is a OpPhi instruction in which A is a block operand. + // control -> control: B is control-dependent on A. + uint32_t GetDivergenceSource(uint32_t id) { + auto it = divergence_source_.find(id); + if (it == divergence_source_.end()) { + return 0; + } + return it->second; + } + + // Returns the dependence source for the control dependence for the given id. + // This only exists for data -> control edges. + // + // In other words, if block 2 is dependent on block 1 due to value 3 (e.g. + // block 1 terminates with OpBranchConditional %3 %2 %4): + // * GetDivergenceSource(2) = 3 + // * GetDivergenceDependenceSource(2) = 1 + // + // Returns 0 if not applicable. + uint32_t GetDivergenceDependenceSource(uint32_t id) { + auto it = divergence_dependence_source_.find(id); + if (it == divergence_dependence_source_.end()) { + return 0; + } + return it->second; + } + + void InitializeWorklist(opt::Function* function, + bool is_first_iteration) override { + // Since |EnqueueSuccessors| is complete, we only need one pass. + if (is_first_iteration) { + Setup(function); + opt::ForwardDataFlowAnalysis::InitializeWorklist(function, true); + } + } + + void EnqueueSuccessors(opt::Instruction* inst) override; + + VisitResult Visit(opt::Instruction* inst) override; + + private: + VisitResult VisitBlock(uint32_t id); + VisitResult VisitInstruction(opt::Instruction* inst); + + // Computes the divergence level for the result of the given instruction + // based on the current state of the analysis. This is always an + // underapproximation, which will be improved as the analysis proceeds. + DivergenceLevel ComputeInstructionDivergence(opt::Instruction* inst); + + // Computes the divergence level for a variable, which is used for loads. + DivergenceLevel ComputeVariableDivergence(opt::Instruction* var); + + // Initializes data structures for performing dataflow on the given function. + void Setup(opt::Function* function); + + std::unordered_map divergence_; + std::unordered_map divergence_source_; + std::unordered_map divergence_dependence_source_; + + // Stores the result of following unconditional branches starting from the + // given block. This is used to detect when reconvergence needs to be + // accounted for. + std::unordered_map follow_unconditional_branches_; + + opt::ControlDependenceAnalysis cd_; +}; + +std::ostream& operator<<(std::ostream& os, + DivergenceAnalysis::DivergenceLevel level); + +} // namespace lint +} // namespace spvtools + +#endif // SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ diff --git a/test/lint/CMakeLists.txt b/test/lint/CMakeLists.txt index b9ef208c..09bc6d39 100644 --- a/test/lint/CMakeLists.txt +++ b/test/lint/CMakeLists.txt @@ -13,6 +13,6 @@ # limitations under the License. add_spvtools_unittest(TARGET lint - SRCS placeholder_test.cpp + SRCS divergence_analysis_test.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ) diff --git a/test/lint/divergence_analysis_test.cpp b/test/lint/divergence_analysis_test.cpp new file mode 100644 index 00000000..36cd32dd --- /dev/null +++ b/test/lint/divergence_analysis_test.cpp @@ -0,0 +1,700 @@ +// Copyright (c) 2021 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/lint/divergence_analysis.h" + +#include + +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace lint { +namespace { + +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +} + +class DivergenceTest : public ::testing::Test { + protected: + std::unique_ptr context_; + std::unique_ptr divergence_; + + void Build(std::string text, uint32_t function_id = 1) { + context_ = BuildModule(SPV_ENV_UNIVERSAL_1_0, CLIMessageConsumer, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context_.get()); + opt::Module* module = context_->module(); + ASSERT_NE(nullptr, module); + // First function should have the given ID. + ASSERT_NE(module->begin(), module->end()); + opt::Function* function = &*module->begin(); + ASSERT_EQ(function->result_id(), function_id); + divergence_.reset(new DivergenceAnalysis(*context_)); + divergence_->Run(function); + } +}; + +// Makes assertions a bit shorter. +using Level = DivergenceAnalysis::DivergenceLevel; + +namespace { +std::string Preamble() { + return R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" %x %y + OpExecutionMode %1 OriginLowerLeft + OpDecorate %y Flat + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %bool = OpTypeBool + %float = OpTypeFloat 32 + %false = OpConstantFalse %bool + %true = OpConstantTrue %bool + %zero = OpConstant %float 0 + %one = OpConstant %float 1 + %x_t = OpTypePointer Input %float + %x = OpVariable %x_t Input + %y = OpVariable %x_t Input + %1 = OpFunction %void None %void_f + )"; +} +} // namespace + +TEST_F(DivergenceTest, SimpleTest) { + // pseudocode: + // %10: + // %11 = load x + // if (%12 = (%11 < 0)) { + // %13: + // // do nothing + // } + // %14: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )")); + // Control flow divergence. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(13)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + // Value divergence. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); +} + +TEST_F(DivergenceTest, FlowTypesTest) { + // pseudocode: + // %10: + // %11 = load x + // %12 = x < 0 // data -> data + // if (%12) { + // %13: // data -> control + // if (true) { + // %14: // control -> control + // } + // %15: + // %16 = 1 + // } else { + // %17: + // %18 = 2 + // } + // %19: + // %19 = phi(%16 from %15, %18 from %17) // control -> data + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %19 None + OpBranchConditional %12 %13 %17 + %13 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %true %14 %15 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + %16 = OpFAdd %float %zero %zero + OpBranch %19 + %17 = OpLabel + %18 = OpFAdd %float %zero %one + OpBranch %19 + %19 = OpLabel + %20 = OpPhi %float %16 %15 %18 %17 + OpReturn + OpFunctionEnd + )")); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(13)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(13)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(13, divergence_->GetDivergenceSource(14)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(15)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(17)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(17)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(20)); + EXPECT_TRUE(divergence_->GetDivergenceSource(20) == 15 || + divergence_->GetDivergenceDependenceSource(20) == 17) + << "Got: " << divergence_->GetDivergenceDependenceSource(20); +} + +TEST_F(DivergenceTest, ExitDependenceTest) { + // pseudocode: + // %10: + // %11 = load x + // %12 = %11 < 0 + // %13: + // do { + // %14: + // if (%12) { + // %15: + // continue; + // } + // %16: + // %17: + // continue; + // } %18: while(false); + // %19: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero ; data -> data + OpBranch %13 + %13 = OpLabel + OpLoopMerge %19 %18 None + OpBranch %14 + %14 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %12 %15 %16 + %15 = OpLabel + OpBranch %18 ; continue + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %18 ; continue + %18 = OpLabel + OpBranchConditional %false %13 %19 + %19 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); + + // Since both branches continue, there's no divergent control dependence + // to 13. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(15)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(15)); + + // These two blocks are outside the if but are still control dependent. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(16)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(16)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(17)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(17)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19)); +} + +TEST_F(DivergenceTest, ReconvergencePromotionTest) { + // pseudocode: + // %10: + // %11 = load y + // %12 = %11 < 0 + // if (%12) { + // %13: + // %14: + // %15: + // if (true) { + // %16: + // } + // // Reconvergence *not* guaranteed as + // // control is not uniform on the IG level + // // at %15. + // %17: + // %18: + // %19: + // %20 = load x + // } + // %21: + // %22 = phi(%11, %20) + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %y + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %21 None + OpBranchConditional %12 %13 %21 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %true %16 %17 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + %20 = OpLoad %float %y + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %float %11 %10 %20 %19 + OpReturn + OpFunctionEnd + )")); + ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(21)); + + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11)); + ASSERT_EQ(0, divergence_->GetDivergenceSource(11)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12)); + ASSERT_EQ(11, divergence_->GetDivergenceSource(12)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(13)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(13)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(14)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(14)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(15)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16)); + ASSERT_EQ(15, divergence_->GetDivergenceSource(16)); + + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(17)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(18)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(18)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(19)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(19)); + + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(20)); + ASSERT_EQ(0, divergence_->GetDivergenceSource(20)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(22)); + ASSERT_EQ(19, divergence_->GetDivergenceSource(22)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); +} + +TEST_F(DivergenceTest, FunctionCallTest) { + // pseudocode: + // %2() { + // %20: + // %21 = load x + // %22 = %21 < 0 + // if (%22) { + // %23: + // return + // } + // %24: + // return + // } + // + // main() { + // %10: + // %11 = %2(); + // // Reconvergence *not* guaranteed. + // %12: + // return + // } + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpFunctionCall %void %2 + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + + %2 = OpFunction %void None %void_f + %20 = OpLabel + %21 = OpLoad %float %x + %22 = OpFOrdLessThan %bool %21 %zero + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + OpReturn + %24 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + // Conservatively assume function return value is uniform. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11)); + // TODO(dongja): blocks reachable from diverging function calls should be + // divergent. + // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); // Wrong! +} + +TEST_F(DivergenceTest, LateMergeTest) { + // pseudocode: + // %10: + // %11 = load y + // %12 = %11 < 0 + // [merge: %15] + // if (%12) { + // %13: + // } + // %14: // Reconvergence hasn't happened by here. + // %15: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %15 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + // TODO(dongja): + // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); // Wrong! + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15)); +} + +// The following series of tests makes sure that we find the least fixpoint. +TEST_F(DivergenceTest, UniformFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < 1 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %one + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, PartiallyUniformFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %21 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %21 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, DivergentFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %20 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %20 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, DivergentOverridesPartiallyUniformTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // %11: + // do { + // %12: + // %13 = phi(%21 from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %20 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %20 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, NestedFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %22: + // %23: + // %24 = phi(%zero from %22, %25 from %26) + // %11: + // do { + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %24 + // } %16: while (%15) + // %17: + // %25 = load x + // } %26: while (false) + // %27: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %22 + %22 = OpLabel + %24 = OpPhi %float %zero %10 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %23 + %23 = OpLabel + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %23 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %24 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + %25 = OpLoad %float %x + OpBranch %26 + %26 = OpLabel + OpBranchConditional %false %22 %27 + %27 = OpLabel + OpReturn + OpFunctionEnd + )")); + // This test makes sure that divergent values flowing upward can influence the + // fixpoint of a loop. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + // Control of the outer loop is still uniform. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(22)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(23)); + // Seed divergent values. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(24)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(25)); + // Outer loop control. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(26)); + // Merged. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(27)); +} + +} // namespace +} // namespace lint +} // namespace spvtools diff --git a/test/lint/placeholder_test.cpp b/test/lint/placeholder_test.cpp deleted file mode 100644 index a2ebec24..00000000 --- a/test/lint/placeholder_test.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2021 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 "gtest/gtest.h" - -namespace spvtools { -namespace lint { -namespace { - -TEST(PlaceholderTest, PlaceholderTest) { ASSERT_TRUE(true); } - -} // namespace -} // namespace lint -} // namespace spvtools -- cgit v1.2.3 From a1d5d67aeb45e4a7681eb41a296561863b2d7519 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Mon, 23 Aug 2021 15:38:26 -0600 Subject: spirv-val: Validate vulkan debug info similarly to opencl debug info (#4466) Co-authored-by: baldurk --- source/ext_inst.cpp | 1 + ...t.nonsemantic.vulkan.debuginfo.100.grammar.json | 3 +- source/val/validate_adjacency.cpp | 5 +- source/val/validate_extensions.cpp | 1074 ++-- source/val/validate_layout.cpp | 75 +- test/val/CMakeLists.txt | 1 + test/val/val_ext_inst_debug_test.cpp | 5313 ++++++++++++++++++++ test/val/val_ext_inst_test.cpp | 3157 +----------- 8 files changed, 6166 insertions(+), 3463 deletions(-) create mode 100644 test/val/val_ext_inst_debug_test.cpp diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index f5f43316..8246c204 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -155,6 +155,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_DEBUGINFO) { return true; } diff --git a/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json b/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json index 371aa423..1d7914d7 100644 --- a/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json +++ b/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json @@ -47,7 +47,8 @@ "operands" : [ { "kind" : "IdRef", "name" : "'Name'" }, { "kind" : "IdRef", "name" : "'Size'" }, - { "kind" : "IdRef", "name" : "'Encoding'" } + { "kind" : "IdRef", "name" : "'Encoding'" }, + { "kind" : "IdRef", "name" : "'Flags'" } ] }, { diff --git a/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp index 64655b0d..13720f0e 100644 --- a/source/val/validate_adjacency.cpp +++ b/source/val/validate_adjacency.cpp @@ -60,7 +60,10 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) { // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need // to discuss the location of DebugScope, DebugNoScope, DebugDeclare, // and DebugValue. - if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) { + // NOTE: This does not apply to the non-semantic vulkan debug info. + if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) || + inst.ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { adjacency_status = PHI_AND_VAR_INVALID; } break; diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index 1f7b9563..5ea23e1f 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -20,7 +20,9 @@ #include "spirv/unified1/NonSemanticClspvReflection.h" +#include "NonSemanticVulkanDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/common_debug_info.h" #include "source/diagnostic.h" #include "source/enum_string_mapping.h" #include "source/extensions.h" @@ -45,6 +47,34 @@ uint32_t GetSizeTBitWidth(const ValidationState_t& _) { return 0; } +bool IsIntScalar(ValidationState_t& _, uint32_t id, bool must_len32, + bool must_unsigned) { + auto type = _.FindDef(id); + if (!type || type->opcode() != SpvOpTypeInt) { + return false; + } + + if (must_len32 && type->GetOperandAs(1) != 32) { + return false; + } + + return !must_unsigned || type->GetOperandAs(2) == 0; +} + +bool IsUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst || inst->opcode() != SpvOpConstant) { + return false; + } + + return IsIntScalar(_, inst->type_id(), true, true); +} + +uint32_t GetUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + return inst->word(3); +} + // 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( @@ -68,6 +98,22 @@ spv_result_t ValidateOperandForDebugInfo( return SPV_SUCCESS; } +// For NonSemantic.Vulkan.DebugInfo.100 check that the operand of a debug info +// instruction |inst| at |word_index| is a result id of a 32-bit integer +// OpConstant instruction. For OpenCL.DebugInfo.100 the parameter is a literal +// word so cannot be validated. +spv_result_t ValidateUint32ConstantOperandForDebugInfo( + ValidationState_t& _, const std::string& operand_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + if (!IsUint32Constant(_, inst->word(word_index))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand " << operand_name + << " must be a result id of 32-bit unsigned OpConstant"; + } + return SPV_SUCCESS; +} + #define CHECK_OPERAND(NAME, opcode, index) \ do { \ auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \ @@ -75,18 +121,27 @@ spv_result_t ValidateOperandForDebugInfo( if (result != SPV_SUCCESS) return result; \ } while (0) +#define CHECK_CONST_UINT_OPERAND(NAME, index) \ + if (vulkanDebugInfo) { \ + auto result = ValidateUint32ConstantOperandForDebugInfo( \ + _, NAME, inst, index, ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } + // 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 std::function& expectation, const Instruction* inst, uint32_t word_index) { if (inst->words().size() <= word_index) return false; 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)))) { + (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 && + debug_inst->ext_inst_type() != + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) || + !expectation(CommonDebugInfoInstructions(debug_inst->word(4)))) { return false; } return true; @@ -97,20 +152,18 @@ bool DoesDebugInfoOperandMatchExpectation( // is |expected_debug_inst|. spv_result_t ValidateDebugInfoOperand( ValidationState_t& _, const std::string& debug_inst_name, - OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst, + CommonDebugInfoInstructions 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) { + std::function expectation = + [expected_debug_inst](CommonDebugInfoInstructions 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 || + if (_.grammar().lookupExtInst(inst->ext_inst_type(), expected_debug_inst, + &desc) != SPV_SUCCESS || !desc) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " @@ -134,9 +187,8 @@ spv_result_t ValidateDebugInfoOperand( 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); + return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic, + inst, word_index, ext_inst_name); } // Check that the operand of a debug info instruction |inst| at |word_index| @@ -147,12 +199,12 @@ 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; + std::function expectation = + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugCompilationUnit || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugLexicalBlock || + dbg_inst == CommonDebugInfoDebugTypeComposite; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; @@ -171,16 +223,15 @@ spv_result_t ValidateOperandDebugType( const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name, bool allow_template_param) { - std::function expectation = - [&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) { + std::function expectation = + [&allow_template_param](CommonDebugInfoInstructions dbg_inst) { if (allow_template_param && - (dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter || - dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) { + (dbg_inst == CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == CommonDebugInfoDebugTypeTemplateTemplateParameter)) { return true; } - return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst && - dbg_inst <= OpenCLDebugInfo100DebugTypeTemplate; + return CommonDebugInfoDebugTypeBasic <= dbg_inst && + dbg_inst <= CommonDebugInfoDebugTypeTemplate; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; @@ -191,28 +242,6 @@ spv_result_t ValidateOperandDebugType( << " is not a valid debug type"; } -bool IsUint32Constant(ValidationState_t& _, uint32_t id) { - auto inst = _.FindDef(id); - if (!inst || inst->opcode() != SpvOpConstant) { - return false; - } - - auto type = _.FindDef(inst->type_id()); - if (!type || type->opcode() != SpvOpTypeInt) { - return false; - } - - if (type->GetOperandAs(1) != 32) { - return false; - } - - if (type->GetOperandAs(2) != 0) { - return false; - } - - return true; -} - spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, const Instruction* inst) { const auto kernel_id = inst->GetOperandAs(4); @@ -667,18 +696,26 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _, const Instruction* inst, uint32_t word_index) { auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index)); - if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == - OpenCLDebugInfo100DebugLocalVariable || - OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == - OpenCLDebugInfo100DebugGlobalVariable) { + if (CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugGlobalVariable) { auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6)); - if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) == - OpenCLDebugInfo100DebugTypeBasic && - (OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == - OpenCLDebugInfo100Signed || - OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == - OpenCLDebugInfo100Unsigned)) { - return true; + if (CommonDebugInfoInstructions(dbg_type->word(4)) == + CommonDebugInfoDebugTypeBasic) { + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100; + uint32_t encoding = dbg_type->word(7); + if (!vulkanDebugInfo || IsUint32Constant(_, encoding)) { + auto ocl_encoding = OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo ? GetUint32Constant(_, encoding) : encoding); + if (ocl_encoding == OpenCLDebugInfo100Signed || + ocl_encoding == OpenCLDebugInfo100Unsigned) { + return true; + } + } } } return false; @@ -2668,7 +2705,9 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { break; } } - } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { if (!_.IsVoidType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " @@ -2676,447 +2715,552 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { << "OpTypeVoid"; } - auto num_words = inst->words().size(); + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100; - 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; + auto num_words = inst->words().size(); - 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, false); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - for (uint32_t i = 6; i < num_words; ++i) { - bool invalid = false; - auto* component_count = _.FindDef(inst->word(i)); - if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { - // TODO: We need a spec discussion for the bindless array. - if (!component_count->word(3)) { - invalid = true; + // Handle any non-common OpenCL insts, then common + if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + OpenCLDebugInfo100Instructions(ext_inst_index) != + OpenCLDebugInfo100DebugModuleINTEL) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case CommonDebugInfoDebugInfoNone: + case CommonDebugInfoDebugNoScope: + break; + // The binary parser validates the opcode for DebugInfoNone, + // DebugNoScope, DebugOperation. We just check the parameters to + // DebugOperation are properly constants for vulkan debug info. + case CommonDebugInfoDebugOperation: { + CHECK_CONST_UINT_OPERAND("Operation", 5); + for (uint32_t i = 6; i < num_words; ++i) { + CHECK_CONST_UINT_OPERAND("Operand", i); + } + break; + } + case CommonDebugInfoDebugCompilationUnit: { + CHECK_CONST_UINT_OPERAND("Version", 5); + CHECK_CONST_UINT_OPERAND("DWARF Version", 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Language", 8); + break; + } + case CommonDebugInfoDebugSource: { + CHECK_OPERAND("File", SpvOpString, 5); + if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6); + break; + } + case CommonDebugInfoDebugTypeBasic: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_OPERAND("Size", SpvOpConstant, 6); + CHECK_CONST_UINT_OPERAND("Encoding", 7); + break; + } + case CommonDebugInfoDebugTypePointer: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Storage Class", 6); + CHECK_CONST_UINT_OPERAND("Flags", 7); + break; + } + case CommonDebugInfoDebugTypeQualifier: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Type Qualifier", 6); + break; + } + case CommonDebugInfoDebugTypeVector: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + + CHECK_CONST_UINT_OPERAND("Component Count", 6); + uint32_t component_count = inst->word(6); + if (vulkanDebugInfo) { + uint64_t const_val; + if (!_.GetConstantValUint64(component_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": Component Count must be 32-bit integer OpConstant"; } - } else if (component_count->words().size() > 6 && - (OpenCLDebugInfo100Instructions(component_count->word( - 4)) == OpenCLDebugInfo100DebugLocalVariable || - OpenCLDebugInfo100Instructions(component_count->word( - 4)) == OpenCLDebugInfo100DebugGlobalVariable)) { - auto* component_count_type = _.FindDef(component_count->word(6)); - if (component_count_type->words().size() > 7) { - if (OpenCLDebugInfo100Instructions(component_count_type->word( - 4)) != OpenCLDebugInfo100DebugTypeBasic || - OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( - component_count_type->word(7)) != - OpenCLDebugInfo100Unsigned) { + component_count = const_val & 0xffffffff; + } + + 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 CommonDebugInfoDebugTypeArray: { + auto validate_base_type = ValidateOperandDebugType( + _, "Base Type", inst, 5, ext_inst_name, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + for (uint32_t i = 6; i < num_words; ++i) { + bool invalid = false; + auto* component_count = _.FindDef(inst->word(i)); + if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { + // TODO: We need a spec discussion for the bindless array. + if (!component_count->word(3)) { invalid = true; - } else { - // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable - // must have Unsigned encoding and 32 or 64 as its size in bits. - Instruction* size_in_bits = - _.FindDef(component_count_type->word(6)); - if (!_.IsIntScalarType(size_in_bits->type_id()) || - (size_in_bits->word(3) != 32 && - size_in_bits->word(3) != 64)) { + } + } else if (component_count->words().size() > 6 && + (CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugGlobalVariable)) { + auto* component_count_type = _.FindDef(component_count->word(6)); + if (component_count_type->words().size() > 7) { + uint32_t encoding = component_count_type->word(7); + if (CommonDebugInfoInstructions(component_count_type->word( + 4)) != CommonDebugInfoDebugTypeBasic || + (vulkanDebugInfo && !IsUint32Constant(_, encoding)) || + OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo + ? GetUint32Constant(_, encoding) + : encoding) != OpenCLDebugInfo100Unsigned) { invalid = true; + } else { + // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable + // must have Unsigned encoding and 32 or 64 as its size in + // bits. + Instruction* size_in_bits = + _.FindDef(component_count_type->word(6)); + if (!_.IsIntScalarType(size_in_bits->type_id()) || + (size_in_bits->word(3) != 32 && + size_in_bits->word(3) != 64)) { + invalid = true; + } } + } else { + invalid = true; } } else { invalid = true; } - } else { - invalid = true; + if (invalid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be " + << "OpConstant with a 32- or 64-bits integer scalar type " + "or " + << "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or " + << "64-bits unsigned integer scalar type"; + } } - if (invalid) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Component Count must be " - << "OpConstant with a 32- or 64-bits integer scalar type or " - << "DebugGlobalVariable or DebugLocalVariable with a 32- or " - << "64-bits unsigned integer scalar type"; + break; + } + case CommonDebugInfoDebugTypedef: { + 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", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case CommonDebugInfoDebugTypeFunction: { + CHECK_CONST_UINT_OPERAND("Flags", 5); + auto* return_type = _.FindDef(inst->word(6)); + // TODO: We need a spec discussion that we have to allow return and + // parameter types of a DebugTypeFunction to have template parameter. + if (return_type->opcode() != SpvOpTypeVoid) { + auto validate_return = ValidateOperandDebugType( + _, "Return Type", inst, 6, ext_inst_name, true); + 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, true); + if (validate_param != SPV_SUCCESS) return validate_param; + } + break; } - 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)); - // TODO: We need a spec discussion that we have to allow return and - // parameter types of a DebugTypeFunction to have template parameter. - if (return_type->opcode() != SpvOpTypeVoid) { - auto validate_return = ValidateOperandDebugType( - _, "Return Type", inst, 6, ext_inst_name, true); - 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, true); - 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, false); - 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); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 12)) { - CHECK_OPERAND("Size", SpvOpConstant, 12); - } - for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + case CommonDebugInfoDebugTypeEnum: { + CHECK_OPERAND("Name", SpvOpString, 5); if (!DoesDebugInfoOperandMatchExpectation( _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugTypeMember || - dbg_inst == OpenCLDebugInfo100DebugFunction || - dbg_inst == OpenCLDebugInfo100DebugTypeInheritance; + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; }, - inst, word_index)) { + inst, 6)) { + auto validate_underlying_type = ValidateOperandDebugType( + _, "Underlying Types", inst, 6, ext_inst_name, false); + if (validate_underlying_type != SPV_SUCCESS) + return validate_underlying_type; + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + 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 Members " - << "must be DebugTypeMember, DebugFunction, or " - "DebugTypeInheritance"; + << ext_inst_name() << ": expected operand Size is a " + << "positive integer"; } - } - break; - } - case OpenCLDebugInfo100DebugTypeMember: { - CHECK_OPERAND("Name", SpvOpString, 5); - // TODO: We need a spec discussion that we have to allow member types - // to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - 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, false); - 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); - 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, false); - 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: { - 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); - // TODO: We need a spec discussion that we have to allow local variable - // types to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - 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); - 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; - } - case OpenCLDebugInfo100DebugTypeTemplate: { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugTypeComposite || - dbg_inst == OpenCLDebugInfo100DebugFunction; - }, - inst, 5)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Target must be DebugTypeComposite " - << "or DebugFunction"; - } - for (uint32_t word_index = 6; word_index < num_words; ++word_index) { + CHECK_CONST_UINT_OPERAND("Flags", 12); + 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 CommonDebugInfoDebugTypeComposite: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); if (!DoesDebugInfoOperandMatchExpectation( _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateParameter || - dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateTemplateParameter; + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; }, - inst, word_index)) { + inst, 12)) { + CHECK_OPERAND("Size", SpvOpConstant, 12); + } + CHECK_CONST_UINT_OPERAND("Flags", 13); + for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeMember || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugTypeInheritance; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Members " + << "must be DebugTypeMember, DebugFunction, or " + "DebugTypeInheritance"; + } + } + break; + } + case CommonDebugInfoDebugTypeMember: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow member types + // to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + // NonSemantic.Vulkan.DebugInfo doesn't have the Parent operand + if (vulkanDebugInfo) { + CHECK_OPERAND("Offset", SpvOpConstant, 10); + CHECK_OPERAND("Size", SpvOpConstant, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + if (num_words == 14) CHECK_OPERAND("Value", SpvOpConstant, 13); + } else { + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, + 10); + CHECK_OPERAND("Offset", SpvOpConstant, 11); + CHECK_OPERAND("Size", SpvOpConstant, 12); + CHECK_CONST_UINT_OPERAND("Flags", 13); + if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14); + } + break; + } + case CommonDebugInfoDebugTypeInheritance: { + CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 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", CommonDebugInfoDebugTypeComposite, 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 Parameters must be " - << "DebugTypeTemplateParameter or " - << "DebugTypeTemplateTemplateParameter"; + << "expected operand Parent must be class or struct debug " + "type"; } + CHECK_OPERAND("Offset", SpvOpConstant, 7); + CHECK_OPERAND("Size", SpvOpConstant, 8); + CHECK_CONST_UINT_OPERAND("Flags", 9); + break; + } + case CommonDebugInfoDebugFunction: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + 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_CONST_UINT_OPERAND("Flags", 12); + CHECK_CONST_UINT_OPERAND("Scope Line", 13); + // NonSemantic.Vulkan.DebugInfo.100 doesn't include a reference to the + // OpFunction + if (vulkanDebugInfo) { + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 14); + } + } else { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 14)) { + CHECK_OPERAND("Function", SpvOpFunction, 14); + } + if (num_words == 16) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 15); + } + } + break; + } + case CommonDebugInfoDebugFunctionDeclaration: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + 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_CONST_UINT_OPERAND("Flags", 12); + break; + } + case CommonDebugInfoDebugLexicalBlock: { + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); + CHECK_CONST_UINT_OPERAND("Line", 6); + CHECK_CONST_UINT_OPERAND("Column", 7); + 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 CommonDebugInfoDebugScope: { + 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", CommonDebugInfoDebugInlinedAt, 6); + } + break; + } + case CommonDebugInfoDebugLocalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow local + // variable types to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_CONST_UINT_OPERAND("Flags", 11); + if (num_words == 13) { + CHECK_CONST_UINT_OPERAND("ArgNumber", 12); + } + break; } - break; - } - case OpenCLDebugInfo100DebugTypeTemplateParameter: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_actual_type = ValidateOperandDebugType( - _, "Actual Type", inst, 6, ext_inst_name, false); - if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 7)) { - CHECK_OPERAND("Value", SpvOpConstant, 7); - } - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8); - break; - } - case OpenCLDebugInfo100DebugGlobalVariable: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 12)) { - auto* operand = _.FindDef(inst->word(12)); + case CommonDebugInfoDebugDeclare: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + auto* operand = _.FindDef(inst->word(6)); if (operand->opcode() != SpvOpVariable && - operand->opcode() != SpvOpConstant) { + operand->opcode() != SpvOpFunctionParameter) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " << "expected operand Variable must be a result id of " - "OpVariable or OpConstant or DebugInfoNone"; + "OpVariable or OpFunctionParameter"; } + + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + + if (vulkanDebugInfo) { + for (uint32_t word_index = 8; word_index < num_words; + ++word_index) { + auto index_inst = _.FindDef(inst->word(word_index)); + auto type_id = index_inst != nullptr ? index_inst->type_id() : 0; + if (type_id == 0 || !IsIntScalar(_, type_id, false, false)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected index must be scalar integer"; + } + } + break; } - if (num_words == 15) { - CHECK_DEBUG_OPERAND("Static Member Declaration", - OpenCLDebugInfo100DebugTypeMember, 14); - } - break; - } - case OpenCLDebugInfo100DebugInlinedAt: { - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - if (num_words == 8) { - CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7); + case CommonDebugInfoDebugExpression: { + for (uint32_t word_index = 5; word_index < num_words; ++word_index) { + CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation, + word_index); + } + break; } - break; - } - case OpenCLDebugInfo100DebugValue: { - CHECK_DEBUG_OPERAND("Local Variable", - OpenCLDebugInfo100DebugLocalVariable, 5); - CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); - - for (uint32_t word_index = 8; word_index < num_words; ++word_index) { - // TODO: The following code simply checks if it is a const int scalar - // or a DebugLocalVariable or DebugGlobalVariable, but we have to - // check it using the same validation for Indexes of OpAccessChain. - if (!IsConstWithIntScalarType(_, inst, word_index) && - !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + case CommonDebugInfoDebugTypeTemplate: { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeComposite || + dbg_inst == CommonDebugInfoDebugFunction; + }, + inst, 5)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand Indexes is " - << "OpConstant, DebugGlobalVariable, or " - << "type is OpConstant with an integer scalar type"; + << ext_inst_name() << ": " + << "expected operand Target must be DebugTypeComposite " + << "or DebugFunction"; + } + for (uint32_t word_index = 6; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == + CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == + CommonDebugInfoDebugTypeTemplateTemplateParameter; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parameters must be " + << "DebugTypeTemplateParameter or " + << "DebugTypeTemplateTemplateParameter"; + } } + break; } - break; + case CommonDebugInfoDebugTypeTemplateParameter: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_actual_type = ValidateOperandDebugType( + _, "Actual Type", inst, 6, ext_inst_name, false); + if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 7)) { + CHECK_OPERAND("Value", SpvOpConstant, 7); + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8); + CHECK_CONST_UINT_OPERAND("Line", 9); + CHECK_CONST_UINT_OPERAND("Column", 10); + break; + } + case CommonDebugInfoDebugGlobalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + auto* operand = _.FindDef(inst->word(12)); + if (operand->opcode() != SpvOpVariable && + operand->opcode() != SpvOpConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpConstant or DebugInfoNone"; + } + } + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Static Member Declaration", + CommonDebugInfoDebugTypeMember, 14); + } + break; + } + case CommonDebugInfoDebugInlinedAt: { + CHECK_CONST_UINT_OPERAND("Line", 5); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 8) { + CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7); + } + break; + } + case CommonDebugInfoDebugValue: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + // TODO: The following code simply checks if it is a const int + // scalar or a DebugLocalVariable or DebugGlobalVariable, but we + // have to check it using the same validation for Indexes of + // OpAccessChain. + if (!IsConstWithIntScalarType(_, inst, word_index) && + !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Indexes is " + << "OpConstant, DebugGlobalVariable, or " + << "type is OpConstant with an integer scalar type"; + } + } + break; + } + + // TODO: Add validation rules for remaining cases as well. + case CommonDebugInfoDebugTypePtrToMember: + case CommonDebugInfoDebugTypeTemplateTemplateParameter: + case CommonDebugInfoDebugTypeTemplateParameterPack: + case CommonDebugInfoDebugLexicalBlockDiscriminator: + case CommonDebugInfoDebugInlinedVariable: + case CommonDebugInfoDebugMacroDef: + case CommonDebugInfoDebugMacroUndef: + case CommonDebugInfoDebugImportedEntity: + break; + case CommonDebugInfoInstructionsMax: + assert(0); + break; } - - // TODO: Add validation rules for remaining cases as well. - case OpenCLDebugInfo100DebugTypePtrToMember: - case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter: - case OpenCLDebugInfo100DebugTypeTemplateParameterPack: - case OpenCLDebugInfo100DebugLexicalBlockDiscriminator: - case OpenCLDebugInfo100DebugInlinedVariable: - case OpenCLDebugInfo100DebugMacroDef: - case OpenCLDebugInfo100DebugMacroUndef: - case OpenCLDebugInfo100DebugImportedEntity: - case OpenCLDebugInfo100DebugModuleINTEL: - break; - case OpenCLDebugInfo100InstructionsMax: - assert(0); - break; } } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { auto import_inst = _.FindDef(inst->GetOperandAs(2)); diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp index b53f991e..e6f4fcad 100644 --- a/source/val/validate_layout.cpp +++ b/source/val/validate_layout.cpp @@ -17,6 +17,7 @@ #include #include "DebugInfo.h" +#include "NonSemanticVulkanDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/diagnostic.h" #include "source/opcode.h" @@ -37,17 +38,7 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, const Instruction* inst, SpvOp opcode) { switch (opcode) { case SpvOpExtInst: - if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { - // non-semantic extinst opcodes are allowed beginning in the types - // section, but since they must name a return type they cannot be the - // first instruction in the types section. Therefore check that we are - // already in it. - if (_.current_layout_section() < kLayoutTypes) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Non-semantic OpExtInst must not appear before types " - << "section"; - } - } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { const uint32_t ext_inst_index = inst->word(4); bool local_debug_info = false; if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { @@ -59,6 +50,18 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, ext_inst_key == OpenCLDebugInfo100DebugValue) { local_debug_info = true; } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + const NonSemanticVulkanDebugInfo100Instructions ext_inst_key = + NonSemanticVulkanDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticVulkanDebugInfo100DebugScope || + ext_inst_key == NonSemanticVulkanDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticVulkanDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticVulkanDebugInfo100DebugValue || + ext_inst_key == + NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } } else { const DebugInfoInstructions ext_inst_key = DebugInfoInstructions(ext_inst_index); @@ -94,6 +97,16 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, << "declarations)"; } } + } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but since they must name a return type they cannot be the + // first instruction in the types section. Therefore check that we are + // already in it. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } } else { // otherwise they must be used in a block if (_.current_layout_section() < kLayoutFunctionDefinitions) { @@ -230,20 +243,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, break; case SpvOpExtInst: - if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { - // non-semantic extinst opcodes are allowed beginning in the types - // section, but must either be placed outside a function declaration, - // or inside a block. - if (_.current_layout_section() < kLayoutTypes) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Non-semantic OpExtInst must not appear before types " - << "section"; - } else if (_.in_function_body() && _.in_block() == false) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Non-semantic OpExtInst within function definition must " - "appear in a block"; - } - } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { const uint32_t ext_inst_index = inst->word(4); bool local_debug_info = false; if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { @@ -255,6 +255,18 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, ext_inst_key == OpenCLDebugInfo100DebugValue) { local_debug_info = true; } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + const NonSemanticVulkanDebugInfo100Instructions ext_inst_key = + NonSemanticVulkanDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticVulkanDebugInfo100DebugScope || + ext_inst_key == NonSemanticVulkanDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticVulkanDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticVulkanDebugInfo100DebugValue || + ext_inst_key == + NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } } else { const DebugInfoInstructions ext_inst_key = DebugInfoInstructions(ext_inst_index); @@ -290,6 +302,19 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, << "declarations)"; } } + } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but must either be placed outside a function declaration, + // or inside a block. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } else if (_.in_function_body() && _.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst within function definition must " + "appear in a block"; + } } else { // otherwise they must be used in a block if (_.in_block() == false) { diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index b1e12ac0..39f9a098 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -45,6 +45,7 @@ add_spvtools_unittest(TARGET val_abcde val_extension_spv_khr_bit_instructions.cpp val_extension_spv_khr_terminate_invocation.cpp val_ext_inst_test.cpp + val_ext_inst_debug_test.cpp ${VAL_TEST_COMMON_SRCS} LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} PCH_FILE pch_test_val diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp new file mode 100644 index 00000000..d3b9f038 --- /dev/null +++ b/test/val/val_ext_inst_debug_test.cpp @@ -0,0 +1,5313 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests validation rules of GLSL.450.std and OpenCL.std extended instructions. +// Doesn't test OpenCL.std vector size 2, 3, 4, 8 or 16 rules (not supported +// by standard SPIR-V). + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateOldDebugInfo = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfo = spvtest::ValidateBase; +using ValidateXDebugInfo = spvtest::ValidateBase; +using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeInheritance = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugGlobalVariable = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugGlobalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugDeclare = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugDeclare = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugValue = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugValue = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfo = spvtest::ValidateBase; + +std::string GenerateShaderCodeForDebugInfo( + const std::string& op_string_instructions, + const std::string& op_const_instructions, + const std::string& debug_instructions_before_main, const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Float16 +OpCapability Float64 +OpCapability Int16 +OpCapability Int64 +)"; + + ss << capabilities_and_extensions; + ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"" + << " %f32_output" + << " %f32vec2_output" + << " %u32_output" + << " %u32vec2_output" + << " %u64_output" + << " %f32_input" + << " %f32vec2_input" + << " %u32_input" + << " %u32vec2_input" + << " %u64_input" + << "\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << op_string_instructions; + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%u16 = OpTypeInt 16 0 +%s16 = OpTypeInt 16 1 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%s32vec2 = OpTypeVector %s32 2 +%u32vec4 = OpTypeVector %u32 4 +%s32vec4 = OpTypeVector %s32 4 +%u64vec2 = OpTypeVector %u64 2 +%s64vec2 = OpTypeVector %s64 2 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 + +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 +%f16_h = OpConstant %f16 0.5 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +)"; + + ss << op_const_instructions; + + ss << R"( +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 + +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 + +%f32_ptr_output = OpTypePointer Output %f32 +%f32vec2_ptr_output = OpTypePointer Output %f32vec2 + +%u32_ptr_output = OpTypePointer Output %u32 +%u32vec2_ptr_output = OpTypePointer Output %u32vec2 + +%u64_ptr_output = OpTypePointer Output %u64 + +%f32_output = OpVariable %f32_ptr_output Output +%f32vec2_output = OpVariable %f32vec2_ptr_output Output + +%u32_output = OpVariable %u32_ptr_output Output +%u32vec2_output = OpVariable %u32vec2_ptr_output Output + +%u64_output = OpVariable %u64_ptr_output Output + +%f32_ptr_input = OpTypePointer Input %f32 +%f32vec2_ptr_input = OpTypePointer Input %f32vec2 + +%u32_ptr_input = OpTypePointer Input %u32 +%u32vec2_ptr_input = OpTypePointer Input %u32vec2 + +%u64_ptr_input = OpTypePointer Input %u64 + +%f32_ptr_function = OpTypePointer Function %f32 + +%f32_input = OpVariable %f32_ptr_input Input +%f32vec2_input = OpVariable %f32vec2_ptr_input Input + +%u32_input = OpVariable %u32_ptr_input Input +%u32vec2_input = OpVariable %u32vec2_ptr_input Input + +%u64_input = OpVariable %u64_ptr_input Input + +%u32_ptr_function = OpTypePointer Function %u32 + +%struct_f16_u16 = OpTypeStruct %f16 %u16 +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 +%struct_f32_u32 = OpTypeStruct %f32 %u32 +%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 +%struct_u32_f32 = OpTypeStruct %u32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 +)"; + + ss << debug_instructions_before_main; + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "DebugInfo" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) { + 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 +)"; + + 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, DebugSourceInFunction) { + 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 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Debug info extension instructions other than DebugScope, " + "DebugNoScope, DebugDeclare, DebugValue must appear between " + "section 9 (types, constants, global variables) and section 10 " + "(function declarations)")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceInFunction) { + 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 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Debug info extension instructions other than DebugScope, " + "DebugNoScope, DebugDeclare, DebugValue must appear between " + "section 9 (types, constants, global variables) and section 10 " + "(function declarations)")); +} + +TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) { + 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" +%int_name = OpString "int" +%foo_name = OpString "foo" +)"; + + 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 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal +%expr = OpExtInst %void %DbgExt DebugExpression +)"; + + const std::string body = R"( +%foo = OpVariable %u32_ptr_function Function +%foo_val = OpLoad %u32 %foo +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header + GetParam(), body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " + "of debug info extension must appear in a function " + "body")); +} + +TEST_P(ValidateLocalDebugInfoOutOfFunction, VulkanDebugInfo100DebugScope) { + 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" +%int_name = OpString "int" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 %u32_1 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%expr = OpExtInst %void %DbgExt DebugExpression +)"; + + const std::string body = R"( +%foo = OpVariable %u32_ptr_function Function +%main_def = OpExtInst %void %DbgExt DebugFunctionDefinition %main_info %main +%foo_val = OpLoad %u32 %foo +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header + GetParam(), body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " + "of debug info extension must appear in a function " + "body")); +} + +INSTANTIATE_TEST_SUITE_P( + AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction, + ::testing::ValuesIn(std::vector{ + "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info", + "%no_scope = OpExtInst %void %DbgExt DebugNoScope", + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { + 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"( +%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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +)"; + + 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, 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 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%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" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + 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 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%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 +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +%foo = OpVariable %f32_ptr_function Function +)"; + + 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, DebugTypeCompositeSizeDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%ty_name = OpString "struct VS_OUTPUT" +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + 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 %VS_OUTPUT_color_info +%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 +%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %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_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + 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 %VS_OUTPUT_color_info +%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 +)"; + + 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_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("forward referenced IDs have not been defined")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceWrongResultType) { + 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 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected result type must be a result id of " + "OpTypeVoid")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceFailFile) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand File must be a result id of " + "OpString")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceFailSource) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Text must be a result id of " + "OpString")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceNoText) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +INSTANTIATE_TEST_SUITE_P(OpenCLAndVkDebugInfo100, ValidateXDebugInfo, + ::testing::ValuesIn(std::vector{ + R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)", + R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)", + })); + +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(ValidateVulkan100DebugInfo, 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 %u32_2 %u32_4 %src %u32_5 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, 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, 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(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %u32_32 %u32_32 %u32_3 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name %u32_3 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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, DebugTypeArrayWithVariableSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%int_name = OpString "int" +%main_name = OpString "main" +%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 +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_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, 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("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +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 OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +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 OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%main_name = OpString "main" +%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 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_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("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_32 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayWithVariableSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%uint_name = OpString "uint" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_6 = OpConstant %u32 6 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_6 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailBaseType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %u32_32 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_6 = OpConstant %u32 6 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +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(ValidateVulkan100DebugInfo, DebugTypeVector) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_5 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive " + "integer less than or equal to 4")); +} + +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(ValidateVulkan100DebugInfo, DebugTypedef) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypedef, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, 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( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypedef, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit)", + "Base Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_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(ValidateVulkan100DebugInfo, DebugTypeFunctionAndParams) { + 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info +%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %float_info +%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void %float_info %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %dbg_src %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %void +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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(ValidateVulkan100DebugInfo, DebugTypeEnum) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name +%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name +%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypeEnum, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeEnum, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Underlying Types"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %dbg_src %u32_32 %u32_3 %u32_0 %foo_name)", + "Parent"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %void %u32_3 %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_0 %u32_3 %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %foo_name %foo_name)", + "Value"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %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_F(ValidateVulkan100DebugInfo, DebugTypeComposite) { + 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3 +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %foo_name %u32_0 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypeComposite, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3 +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, 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( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeComposite, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %comp_unit %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %dbg_src %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %u32_128 %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Linkage Name"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src %u32_3 %VS_OUTPUT_pos_info)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %dbg_src %VS_OUTPUT_pos_info)", + "Flags"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %dbg_src)", + "Members"), + })); + +TEST_P(ValidateVulkan100DebugInfoDebugTypeMember, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, 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( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeMember, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + ""), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %float_info %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %void %u32_32 %u32_3)", + "Offset"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %void %u32_3)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %void)", + "Flags"), + })); + +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(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugFunction, 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 std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugFunction, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3 %u32_13)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3 %u32_13)", + "Linkage Name"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13 %dbg_src)", + "Declaration"), + })); + +TEST_P(ValidateVulkan100DebugInfoDebugFunctionDeclaration, 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 std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, + ValidateVulkan100DebugInfoDebugFunctionDeclaration, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3)", + "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(ValidateVulkan100DebugInfo, DebugLexicalBlock) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src %u32_1 %u32_1 %comp_unit %main_name +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugLexicalBlock, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLexicalBlock, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%comp_unit %u32_1 %u32_1 %comp_unit %main_name)", + "Source"), + std::make_pair(R"(%dbg_src %u32_1 %u32_1 %dbg_src %main_name)", + "Parent"), + std::make_pair(R"(%dbg_src %u32_1 %u32_1 %comp_unit %void)", "Name"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugLocalVariable, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugLocalVariable )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLocalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_10 %dbg_src %u32_3 %u32_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(ValidateVulkan100DebugInfo, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string body = R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugDeclareParam) { + CompileSuccessfully(R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Vulkan.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 + %u32_0 = OpConstant %uint 0 + %u32_1 = OpConstant %uint 1 + %u32_2 = OpConstant %uint 2 + %u32_3 = OpConstant %uint 3 + %u32_4 = OpConstant %uint 4 + %u32_5 = OpConstant %uint 5 + %u32_10 = OpConstant %uint 10 + %u32_17 = OpConstant %uint 17 + %u32_28 = OpConstant %uint 28 + %u32_32 = OpConstant %uint 32 + %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 %u32_1 %u32_4 %12 %u32_5 + %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 %u32_3 %u32_0 + %16 = OpExtInst %void %1 DebugTypeFunction %u32_3 %void %15 + %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 %u32_1 %u32_1 %13 %17 %u32_3 %u32_1 + %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 %u32_1 %u32_17 %18 %u32_4 %u32_0 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_28 %18 + %main = OpFunction %void None %23 + %24 = OpLabel +%param_var_foo = OpVariable %_ptr_Function_float Function + %27 = OpLoad %float %in_var_COLOR + %28 = OpFunctionCall %void %src_main %param_var_foo + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %29 + %foo = OpFunctionParameter %_ptr_Function_float + %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 + %bb_entry = OpLabel + OpReturn + OpFunctionEnd +)"); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugDeclare, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare )" + << param.first; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugDeclare, + ::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_F(ValidateVulkan100DebugInfo, DebugExpression) { + const std::string dbg_inst_header = R"( +%op0 = OpExtInst %void %DbgExt DebugOperation %u32_0 +%op1 = OpExtInst %void %DbgExt DebugOperation %u32_1 +%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugExpressionFail) { + const std::string dbg_inst_header = R"( +%op = OpExtInst %void %DbgExt DebugOperation %u32_0 +%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.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_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%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 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +)"; + + 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, DebugTypeTemplateUsedForVariableType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%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 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + 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, DebugTypeTemplateFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%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 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param +)"; + + 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, DebugTypeTemplateFailTarget) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%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 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param +)"; + + 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 Target must be DebugTypeComposite or " + "DebugFunction")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%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 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_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 Parameters must be DebugTypeTemplateParameter or " + "DebugTypeTemplateTemplateParameter")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplate) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateUsedForVariableType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFailTarget) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Target must be DebugTypeComposite or " + "DebugFunction")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Parameters must be DebugTypeTemplateParameter or " + "DebugTypeTemplateTemplateParameter")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void 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 = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + 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, DebugGlobalVariableStaticMember) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void 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 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a +)"; + + 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, DebugGlobalVariableDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void 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"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%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 DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate +)"; + + 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, DebugGlobalVariableConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void 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 = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate +)"; + + 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(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void 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 = OpExtInst %void %DbgExt DebugGlobalVariable )" + << 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, ValidateOpenCL100DebugInfoDebugGlobalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Scope"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)", + "Linkage Name"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)", + "Variable"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableStaticMember) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %t %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + 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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %dbgNone %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugGlobalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateVulkan100DebugInfoDebugGlobalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %dbg_src %foo_name %f32_input %u32_3)", + "Scope"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %void %f32_input %u32_3)", + "Linkage Name"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %void %u32_3)", + "Variable"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) { + 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"( +%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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + 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, DebugInlinedAtFail) { + 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"( +%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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + 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, DebugInlinedAtFail2) { + 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"( +%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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + 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")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAt) { + 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %inlined_at +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail) { + 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %inlined_at %inlined_at +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail2) { + 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %main_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugValue) { + 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_3 = OpConstant %u32 3 +%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 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3 +)"; + + 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, DebugValueWithVariableIndex) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%int_name = OpString "int" +%foo_name = OpString "foo" +%len_name = OpString "length" +)"; + + const std::string size_const = R"( +%int_3 = OpConstant %u32 3 +%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 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal +%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info +)"; + + 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_P(ValidateOpenCL100DebugInfoDebugValue, 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"( +%decl = OpExtInst %void %DbgExt DebugValue )" + << 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, ValidateOpenCL100DebugInfoDebugValue, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"), + std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugValue) { + 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugValueWithVariableIndex) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%int_name = OpString "int" +%foo_name = OpString "foo" +%len_name = OpString "length" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_32 %u32_4 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0 +%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src %u32_0 %u32_0 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %len_info +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugValue, 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 constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_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 %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%decl = OpExtInst %void %DbgExt DebugValue )" + << param.first; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, 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, ValidateVulkan100DebugInfoDebugValue, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %u32_32 %null_expr %u32_3)", + "Local Variable"), + std::make_pair(R"(%foo_info %u32_32 %dbg_src %u32_3)", "Expression"), + std::make_pair(R"(%foo_info %u32_32 %null_expr %dbg_src)", "Indexes"), + })); + +TEST_F(ValidateVulkan100DebugInfo, VulkanDebugInfoSample) { + std::ostringstream ss; + ss << R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %id_1 = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %id_MainPs "MainPs" %id_in_var_TEXCOORD2 %id_out_var_SV_Target0 + OpExecutionMode %id_MainPs OriginUpperLeft + %id_7 = OpString "foo.frag" + %id_27 = OpString "float" + %id_32 = OpString "vColor" + %id_36 = OpString "PS_OUTPUT" + %id_42 = OpString "vTextureCoords" + %id_46 = OpString "PS_INPUT" + %id_49 = OpString "MainPs" + %id_50 = OpString "" + %id_55 = OpString "ps_output" + %id_59 = OpString "i" + %id_63 = OpString "@type.sampler" + %id_64 = OpString "type.sampler" + %id_66 = OpString "g_sAniso" + %id_69 = OpString "@type.2d.image" + %id_70 = OpString "type.2d.image" + %id_72 = OpString "TemplateParam" + %id_75 = OpString "g_tColor" + OpName %id_type_2d_image "type.2d.image" + OpName %id_g_tColor "g_tColor" + OpName %id_type_sampler "type.sampler" + OpName %id_g_sAniso "g_sAniso" + OpName %id_in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %id_out_var_SV_Target0 "out.var.SV_Target0" + OpName %id_MainPs "MainPs" + OpName %id_PS_INPUT "PS_INPUT" + OpMemberName %id_PS_INPUT 0 "vTextureCoords" + OpName %id_param_var_i "param.var.i" + OpName %id_PS_OUTPUT "PS_OUTPUT" + OpMemberName %id_PS_OUTPUT 0 "vColor" + OpName %id_src_MainPs "src.MainPs" + OpName %id_i "i" + OpName %id_bb_entry "bb.entry" + OpName %id_ps_output "ps_output" + OpName %id_type_sampled_image "type.sampled.image" + OpDecorate %id_in_var_TEXCOORD2 Location 0 + OpDecorate %id_out_var_SV_Target0 Location 0 + OpDecorate %id_g_tColor DescriptorSet 0 + OpDecorate %id_g_tColor Binding 0 + OpDecorate %id_g_sAniso DescriptorSet 0 + OpDecorate %id_g_sAniso Binding 1 + %id_int = OpTypeInt 32 1 + %id_int_0 = OpConstant %id_int 0 + %id_uint = OpTypeInt 32 0 + %id_uint_32 = OpConstant %id_uint 32 + %id_float = OpTypeFloat 32 +%id_type_2d_image = OpTypeImage %id_float 2D 2 0 0 1 Unknown +%id__ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %id_type_2d_image +%id_type_sampler = OpTypeSampler +%id__ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %id_type_sampler + %id_v2float = OpTypeVector %id_float 2 +%id__ptr_Input_v2float = OpTypePointer Input %id_v2float + %id_v4float = OpTypeVector %id_float 4 +%id__ptr_Output_v4float = OpTypePointer Output %id_v4float + %id_void = OpTypeVoid + %id_uint_1 = OpConstant %id_uint 1 + %id_uint_4 = OpConstant %id_uint 4 + %id_uint_5 = OpConstant %id_uint 5 + %id_uint_3 = OpConstant %id_uint 3 + %id_uint_0 = OpConstant %id_uint 0 + %id_uint_128 = OpConstant %id_uint 128 + %id_uint_12 = OpConstant %id_uint 12 + %id_uint_10 = OpConstant %id_uint 10 + %id_uint_8 = OpConstant %id_uint 8 + %id_uint_2 = OpConstant %id_uint 2 + %id_uint_64 = OpConstant %id_uint 64 + %id_uint_7 = OpConstant %id_uint 7 + %id_uint_15 = OpConstant %id_uint 15 + %id_uint_16 = OpConstant %id_uint 16 + %id_uint_17 = OpConstant %id_uint 17 + %id_uint_29 = OpConstant %id_uint 29 + %id_uint_14 = OpConstant %id_uint 14 + %id_uint_11 = OpConstant %id_uint 11 + %id_78 = OpTypeFunction %id_void + %id_PS_INPUT = OpTypeStruct %id_v2float +%id__ptr_Function_PS_INPUT = OpTypePointer Function %id_PS_INPUT + %id_PS_OUTPUT = OpTypeStruct %id_v4float + %id_89 = OpTypeFunction %id_PS_OUTPUT %id__ptr_Function_PS_INPUT +%id__ptr_Function_PS_OUTPUT = OpTypePointer Function %id_PS_OUTPUT + %id_uint_20 = OpConstant %id_uint 20 + %id_uint_19 = OpConstant %id_uint 19 + %id_uint_26 = OpConstant %id_uint 26 + %id_uint_46 = OpConstant %id_uint 46 +%id__ptr_Function_v2float = OpTypePointer Function %id_v2float + %id_uint_57 = OpConstant %id_uint 57 + %id_uint_78 = OpConstant %id_uint 78 +%id_type_sampled_image = OpTypeSampledImage %id_type_2d_image + %id_uint_81 = OpConstant %id_uint 81 +%id__ptr_Function_v4float = OpTypePointer Function %id_v4float + %id_g_tColor = OpVariable %id__ptr_UniformConstant_type_2d_image UniformConstant + %id_g_sAniso = OpVariable %id__ptr_UniformConstant_type_sampler UniformConstant +%id_in_var_TEXCOORD2 = OpVariable %id__ptr_Input_v2float Input +%id_out_var_SV_Target0 = OpVariable %id__ptr_Output_v4float Output + %id_22 = OpExtInst %id_void %id_1 DebugSource %id_7 + %id_23 = OpExtInst %id_void %id_1 DebugCompilationUnit %id_uint_1 %id_uint_4 %id_22 %id_uint_5 + %id_28 = OpExtInst %id_void %id_1 DebugTypeBasic %id_27 %id_uint_32 %id_uint_3 %id_uint_0 + %id_31 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_4 + %id_34 = OpExtInst %id_void %id_1 DebugTypeMember %id_32 %id_31 %id_22 %id_uint_12 %id_uint_12 %id_uint_0 %id_uint_128 %id_uint_3 + %id_37 = OpExtInst %id_void %id_1 DebugTypeComposite %id_36 %id_uint_1 %id_22 %id_uint_10 %id_uint_8 %id_23 %id_36 %id_uint_128 %id_uint_3 %id_34 + %id_40 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_2 + %id_44 = OpExtInst %id_void %id_1 DebugTypeMember %id_42 %id_40 %id_22 %id_uint_7 %id_uint_12 %id_uint_0 %id_uint_64 %id_uint_3 + %id_47 = OpExtInst %id_void %id_1 DebugTypeComposite %id_46 %id_uint_1 %id_22 %id_uint_5 %id_uint_8 %id_23 %id_46 %id_uint_64 %id_uint_3 %id_44 + %id_48 = OpExtInst %id_void %id_1 DebugTypeFunction %id_uint_3 %id_37 %id_47 + %id_51 = OpExtInst %id_void %id_1 DebugFunction %id_49 %id_48 %id_22 %id_uint_15 %id_uint_1 %id_23 %id_50 %id_uint_3 %id_uint_16 + %id_54 = OpExtInst %id_void %id_1 DebugLexicalBlock %id_22 %id_uint_16 %id_uint_1 %id_51 + %id_56 = OpExtInst %id_void %id_1 DebugLocalVariable %id_55 %id_37 %id_22 %id_uint_17 %id_uint_15 %id_54 %id_uint_4 + %id_58 = OpExtInst %id_void %id_1 DebugExpression + %id_60 = OpExtInst %id_void %id_1 DebugLocalVariable %id_59 %id_47 %id_22 %id_uint_15 %id_uint_29 %id_51 %id_uint_4 %id_uint_1 + %id_62 = OpExtInst %id_void %id_1 DebugInfoNone + %id_65 = OpExtInst %id_void %id_1 DebugTypeComposite %id_63 %id_uint_1 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_64 %id_62 %id_uint_3 + %id_67 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_66 %id_65 %id_22 %id_uint_3 %id_uint_14 %id_23 %id_66 %id_g_sAniso %id_uint_8 + %id_71 = OpExtInst %id_void %id_1 DebugTypeComposite %id_69 %id_uint_0 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_70 %id_62 %id_uint_3 + %id_73 = OpExtInst %id_void %id_1 DebugTypeTemplateParameter %id_72 %id_31 %id_62 %id_22 %id_uint_0 %id_uint_0 + %id_74 = OpExtInst %id_void %id_1 DebugTypeTemplate %id_71 %id_73 + %id_76 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_75 %id_74 %id_22 %id_uint_1 %id_uint_11 %id_23 %id_75 %id_g_tColor %id_uint_8 + %id_MainPs = OpFunction %id_void None %id_78 + %id_79 = OpLabel +%id_param_var_i = OpVariable %id__ptr_Function_PS_INPUT Function + %id_83 = OpLoad %id_v2float %id_in_var_TEXCOORD2 + %id_84 = OpCompositeConstruct %id_PS_INPUT %id_83 + OpStore %id_param_var_i %id_84 + %id_86 = OpFunctionCall %id_PS_OUTPUT %id_src_MainPs %id_param_var_i + %id_88 = OpCompositeExtract %id_v4float %id_86 0 + OpStore %id_out_var_SV_Target0 %id_88 + OpReturn + OpFunctionEnd + %id_src_MainPs = OpFunction %id_PS_OUTPUT None %id_89 + %id_i = OpFunctionParameter %id__ptr_Function_PS_INPUT + %id_bb_entry = OpLabel + %id_ps_output = OpVariable %id__ptr_Function_PS_OUTPUT Function + %id_94 = OpExtInst %id_void %id_1 DebugScope %id_51 + %id_97 = OpExtInst %id_void %id_1 DebugDeclare %id_60 %id_i %id_58 + %id_99 = OpExtInst %id_void %id_1 DebugFunctionDefinition %id_51 %id_src_MainPs + %id_100 = OpExtInst %id_void %id_1 DebugScope %id_54 + %id_102 = OpExtInst %id_void %id_1 DebugDeclare %id_56 %id_ps_output %id_58 + %id_106 = OpLoad %id_type_2d_image %id_g_tColor + %id_109 = OpLoad %id_type_sampler %id_g_sAniso + %id_114 = OpAccessChain %id__ptr_Function_v2float %id_i %id_int_0 + %id_115 = OpLoad %id_v2float %id_114 + %id_119 = OpSampledImage %id_type_sampled_image %id_106 %id_109 + %id_120 = OpImageSampleImplicitLod %id_v4float %id_119 %id_115 None + %id_123 = OpAccessChain %id__ptr_Function_v4float %id_ps_output %id_int_0 + OpStore %id_123 %id_120 + %id_125 = OpLoad %id_PS_OUTPUT %id_ps_output + OpReturnValue %id_125 + OpFunctionEnd +)"; + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index 06ecba63..b014ad62 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -33,33 +33,6 @@ using ::testing::HasSubstr; using ::testing::Not; 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 ValidateOpenCL100DebugInfoDebugGlobalVariable = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugDeclare = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugValue = - spvtest::ValidateBase>; using ValidateGlslStd450SqrtLike = spvtest::ValidateBase; using ValidateGlslStd450FMinLike = spvtest::ValidateBase; using ValidateGlslStd450FClampLike = spvtest::ValidateBase; @@ -269,2994 +242,235 @@ OpCapability Int64 %struct_f32_u32 = OpTypeStruct %f32 %u32 %struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 %struct_u32_f32 = OpTypeStruct %u32 %f32 -%struct_u32_u32 = OpTypeStruct %u32 %u32 -%struct_f32_f64 = OpTypeStruct %f32 %f64 -%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 -%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 - -%main = OpFunction %void None %func -%main_entry = OpLabel -)"; - - ss << body; - - ss << R"( -OpReturn -OpFunctionEnd)"; - - return ss.str(); -} - -std::string GenerateKernelCode( - const std::string& body, - const std::string& capabilities_and_extensions = "", - const std::string& memory_model = "Physical32") { - std::ostringstream ss; - ss << R"( -OpCapability Addresses -OpCapability Kernel -OpCapability Linkage -OpCapability GenericPointer -OpCapability Int8 -OpCapability Int16 -OpCapability Int64 -OpCapability Float16 -OpCapability Float64 -OpCapability Vector16 -OpCapability Matrix -)"; - - ss << capabilities_and_extensions; - ss << "%extinst = OpExtInstImport \"OpenCL.std\"\n"; - ss << "OpMemoryModel " << memory_model << " OpenCL\n"; - - ss << R"( -%void = OpTypeVoid -%func = OpTypeFunction %void -%bool = OpTypeBool -%f16 = OpTypeFloat 16 -%f32 = OpTypeFloat 32 -%f64 = OpTypeFloat 64 -%u32 = OpTypeInt 32 0 -%u64 = OpTypeInt 64 0 -%u16 = OpTypeInt 16 0 -%u8 = OpTypeInt 8 0 -%f32vec2 = OpTypeVector %f32 2 -%f32vec3 = OpTypeVector %f32 3 -%f32vec4 = OpTypeVector %f32 4 -%f32vec8 = OpTypeVector %f32 8 -%f16vec8 = OpTypeVector %f16 8 -%f32vec16 = OpTypeVector %f32 16 -%f64vec2 = OpTypeVector %f64 2 -%f64vec3 = OpTypeVector %f64 3 -%f64vec4 = OpTypeVector %f64 4 -%u32vec2 = OpTypeVector %u32 2 -%u32vec3 = OpTypeVector %u32 3 -%u32vec4 = OpTypeVector %u32 4 -%u32vec8 = OpTypeVector %u32 8 -%u64vec2 = OpTypeVector %u64 2 -%f64mat22 = OpTypeMatrix %f64vec2 2 -%f32mat22 = OpTypeMatrix %f32vec2 2 -%f32mat23 = OpTypeMatrix %f32vec2 3 -%f32mat32 = OpTypeMatrix %f32vec3 2 -%f32mat33 = OpTypeMatrix %f32vec3 3 - -%f32_0 = OpConstant %f32 0 -%f32_1 = OpConstant %f32 1 -%f32_2 = OpConstant %f32 2 -%f32_3 = OpConstant %f32 3 -%f32_4 = OpConstant %f32 4 -%f32_h = OpConstant %f32 0.5 -%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 -%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 -%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 -%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 -%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 -%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 -%f32vec8_01010101 = OpConstantComposite %f32vec8 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1 - -%f64_0 = OpConstant %f64 0 -%f64_1 = OpConstant %f64 1 -%f64_2 = OpConstant %f64 2 -%f64_3 = OpConstant %f64 3 -%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 -%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 -%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 - -%f16_0 = OpConstant %f16 0 -%f16_1 = OpConstant %f16 1 - -%u8_0 = OpConstant %u8 0 -%u8_1 = OpConstant %u8 1 -%u8_2 = OpConstant %u8 2 -%u8_3 = OpConstant %u8 3 - -%u16_0 = OpConstant %u16 0 -%u16_1 = OpConstant %u16 1 -%u16_2 = OpConstant %u16 2 -%u16_3 = OpConstant %u16 3 - -%u32_0 = OpConstant %u32 0 -%u32_1 = OpConstant %u32 1 -%u32_2 = OpConstant %u32 2 -%u32_3 = OpConstant %u32 3 -%u32_256 = OpConstant %u32 256 - -%u64_0 = OpConstant %u64 0 -%u64_1 = OpConstant %u64 1 -%u64_2 = OpConstant %u64 2 -%u64_3 = OpConstant %u64 3 -%u64_256 = OpConstant %u64 256 - -%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 -%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 -%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 -%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 - -%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 - -%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 -%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 - -%struct_f32_f32 = OpTypeStruct %f32 %f32 -%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 -%struct_f32_u32 = OpTypeStruct %f32 %u32 -%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 -%struct_u32_f32 = OpTypeStruct %u32 %f32 -%struct_u32_u32 = OpTypeStruct %u32 %u32 -%struct_f32_f64 = OpTypeStruct %f32 %f64 -%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 -%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 - -%f16vec8_ptr_workgroup = OpTypePointer Workgroup %f16vec8 -%f16vec8_workgroup = OpVariable %f16vec8_ptr_workgroup Workgroup -%f16_ptr_workgroup = OpTypePointer Workgroup %f16 - -%u32vec8_ptr_workgroup = OpTypePointer Workgroup %u32vec8 -%u32vec8_workgroup = OpVariable %u32vec8_ptr_workgroup Workgroup -%u32_ptr_workgroup = OpTypePointer Workgroup %u32 - -%f32vec8_ptr_workgroup = OpTypePointer Workgroup %f32vec8 -%f32vec8_workgroup = OpVariable %f32vec8_ptr_workgroup Workgroup -%f32_ptr_workgroup = OpTypePointer Workgroup %f32 - -%u32arr = OpTypeArray %u32 %u32_256 -%u32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32arr -%u32arr_cross_workgroup = OpVariable %u32arr_ptr_cross_workgroup CrossWorkgroup -%u32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32 - -%f32arr = OpTypeArray %f32 %u32_256 -%f32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32arr -%f32arr_cross_workgroup = OpVariable %f32arr_ptr_cross_workgroup CrossWorkgroup -%f32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32 - -%f32vec2arr = OpTypeArray %f32vec2 %u32_256 -%f32vec2arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2arr -%f32vec2arr_cross_workgroup = OpVariable %f32vec2arr_ptr_cross_workgroup CrossWorkgroup -%f32vec2_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2 - -%struct_arr = OpTypeArray %struct_f32_f32 %u32_256 -%struct_arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_arr -%struct_arr_cross_workgroup = OpVariable %struct_arr_ptr_cross_workgroup CrossWorkgroup -%struct_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_f32_f32 - -%f16vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f16vec8 -%f16vec8_uniform_constant = OpVariable %f16vec8_ptr_uniform_constant UniformConstant -%f16_ptr_uniform_constant = OpTypePointer UniformConstant %f16 - -%u32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %u32vec8 -%u32vec8_uniform_constant = OpVariable %u32vec8_ptr_uniform_constant UniformConstant -%u32_ptr_uniform_constant = OpTypePointer UniformConstant %u32 - -%f32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f32vec8 -%f32vec8_uniform_constant = OpVariable %f32vec8_ptr_uniform_constant UniformConstant -%f32_ptr_uniform_constant = OpTypePointer UniformConstant %f32 - -%f16vec8_ptr_input = OpTypePointer Input %f16vec8 -%f16vec8_input = OpVariable %f16vec8_ptr_input Input -%f16_ptr_input = OpTypePointer Input %f16 - -%u32vec8_ptr_input = OpTypePointer Input %u32vec8 -%u32vec8_input = OpVariable %u32vec8_ptr_input Input -%u32_ptr_input = OpTypePointer Input %u32 - -%f32_ptr_generic = OpTypePointer Generic %f32 -%u32_ptr_generic = OpTypePointer Generic %u32 - -%f32_ptr_function = OpTypePointer Function %f32 -%f32vec2_ptr_function = OpTypePointer Function %f32vec2 -%u32_ptr_function = OpTypePointer Function %u32 -%u64_ptr_function = OpTypePointer Function %u64 -%u32vec2_ptr_function = OpTypePointer Function %u32vec2 - -%u8arr = OpTypeArray %u8 %u32_256 -%u8arr_ptr_uniform_constant = OpTypePointer UniformConstant %u8arr -%u8arr_uniform_constant = OpVariable %u8arr_ptr_uniform_constant UniformConstant -%u8_ptr_uniform_constant = OpTypePointer UniformConstant %u8 -%u8_ptr_generic = OpTypePointer Generic %u8 - -%main = OpFunction %void None %func -%main_entry = OpLabel -)"; - - ss << body; - - ss << R"( -OpReturn -OpFunctionEnd)"; - - return ss.str(); -} - -std::string GenerateShaderCodeForDebugInfo( - const std::string& op_string_instructions, - const std::string& op_const_instructions, - const std::string& debug_instructions_before_main, const std::string& body, - const std::string& capabilities_and_extensions = "", - const std::string& execution_model = "Fragment") { - std::ostringstream ss; - ss << R"( -OpCapability Shader -OpCapability Float16 -OpCapability Float64 -OpCapability Int16 -OpCapability Int64 -)"; - - ss << capabilities_and_extensions; - ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; - ss << "OpMemoryModel Logical GLSL450\n"; - ss << "OpEntryPoint " << execution_model << " %main \"main\"" - << " %f32_output" - << " %f32vec2_output" - << " %u32_output" - << " %u32vec2_output" - << " %u64_output" - << " %f32_input" - << " %f32vec2_input" - << " %u32_input" - << " %u32vec2_input" - << " %u64_input" - << "\n"; - if (execution_model == "Fragment") { - ss << "OpExecutionMode %main OriginUpperLeft\n"; - } - - ss << op_string_instructions; - - ss << R"( -%void = OpTypeVoid -%func = OpTypeFunction %void -%bool = OpTypeBool -%f16 = OpTypeFloat 16 -%f32 = OpTypeFloat 32 -%f64 = OpTypeFloat 64 -%u32 = OpTypeInt 32 0 -%s32 = OpTypeInt 32 1 -%u64 = OpTypeInt 64 0 -%s64 = OpTypeInt 64 1 -%u16 = OpTypeInt 16 0 -%s16 = OpTypeInt 16 1 -%f32vec2 = OpTypeVector %f32 2 -%f32vec3 = OpTypeVector %f32 3 -%f32vec4 = OpTypeVector %f32 4 -%f64vec2 = OpTypeVector %f64 2 -%f64vec3 = OpTypeVector %f64 3 -%f64vec4 = OpTypeVector %f64 4 -%u32vec2 = OpTypeVector %u32 2 -%u32vec3 = OpTypeVector %u32 3 -%s32vec2 = OpTypeVector %s32 2 -%u32vec4 = OpTypeVector %u32 4 -%s32vec4 = OpTypeVector %s32 4 -%u64vec2 = OpTypeVector %u64 2 -%s64vec2 = OpTypeVector %s64 2 -%f64mat22 = OpTypeMatrix %f64vec2 2 -%f32mat22 = OpTypeMatrix %f32vec2 2 -%f32mat23 = OpTypeMatrix %f32vec2 3 -%f32mat32 = OpTypeMatrix %f32vec3 2 -%f32mat33 = OpTypeMatrix %f32vec3 3 - -%f32_0 = OpConstant %f32 0 -%f32_1 = OpConstant %f32 1 -%f32_2 = OpConstant %f32 2 -%f32_3 = OpConstant %f32 3 -%f32_4 = OpConstant %f32 4 -%f32_h = OpConstant %f32 0.5 -%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 -%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 -%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 -%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 -%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 -%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 - -%f64_0 = OpConstant %f64 0 -%f64_1 = OpConstant %f64 1 -%f64_2 = OpConstant %f64 2 -%f64_3 = OpConstant %f64 3 -%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 -%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 -%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 - -%f16_0 = OpConstant %f16 0 -%f16_1 = OpConstant %f16 1 -%f16_h = OpConstant %f16 0.5 - -%u32_0 = OpConstant %u32 0 -%u32_1 = OpConstant %u32 1 -%u32_2 = OpConstant %u32 2 -%u32_3 = OpConstant %u32 3 - -%s32_0 = OpConstant %s32 0 -%s32_1 = OpConstant %s32 1 -%s32_2 = OpConstant %s32 2 -%s32_3 = OpConstant %s32 3 - -%u64_0 = OpConstant %u64 0 -%u64_1 = OpConstant %u64 1 -%u64_2 = OpConstant %u64 2 -%u64_3 = OpConstant %u64 3 - -%s64_0 = OpConstant %s64 0 -%s64_1 = OpConstant %s64 1 -%s64_2 = OpConstant %s64 2 -%s64_3 = OpConstant %s64 3 -)"; - - ss << op_const_instructions; - - ss << R"( -%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 -%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 - -%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 -%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 - -%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 -%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 - -%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 -%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 - -%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 -%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 - -%f32_ptr_output = OpTypePointer Output %f32 -%f32vec2_ptr_output = OpTypePointer Output %f32vec2 - -%u32_ptr_output = OpTypePointer Output %u32 -%u32vec2_ptr_output = OpTypePointer Output %u32vec2 - -%u64_ptr_output = OpTypePointer Output %u64 - -%f32_output = OpVariable %f32_ptr_output Output -%f32vec2_output = OpVariable %f32vec2_ptr_output Output - -%u32_output = OpVariable %u32_ptr_output Output -%u32vec2_output = OpVariable %u32vec2_ptr_output Output - -%u64_output = OpVariable %u64_ptr_output Output - -%f32_ptr_input = OpTypePointer Input %f32 -%f32vec2_ptr_input = OpTypePointer Input %f32vec2 - -%u32_ptr_input = OpTypePointer Input %u32 -%u32vec2_ptr_input = OpTypePointer Input %u32vec2 - -%u64_ptr_input = OpTypePointer Input %u64 - -%f32_ptr_function = OpTypePointer Function %f32 - -%f32_input = OpVariable %f32_ptr_input Input -%f32vec2_input = OpVariable %f32vec2_ptr_input Input - -%u32_input = OpVariable %u32_ptr_input Input -%u32vec2_input = OpVariable %u32vec2_ptr_input Input - -%u64_input = OpVariable %u64_ptr_input Input - -%u32_ptr_function = OpTypePointer Function %u32 - -%struct_f16_u16 = OpTypeStruct %f16 %u16 -%struct_f32_f32 = OpTypeStruct %f32 %f32 -%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 -%struct_f32_u32 = OpTypeStruct %f32 %u32 -%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 -%struct_u32_f32 = OpTypeStruct %u32 %f32 -%struct_u32_u32 = OpTypeStruct %u32 %u32 -%struct_f32_f64 = OpTypeStruct %f32 %f64 -%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 -%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 -)"; - - ss << debug_instructions_before_main; - - ss << R"( -%main = OpFunction %void None %func -%main_entry = OpLabel -)"; - - ss << body; - - ss << R"( -OpReturn -OpFunctionEnd)"; - - return ss.str(); -} - -TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) { - const std::string src = R"( -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "DebugInfo" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) { - 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 -)"; - - 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, DebugSourceInFunction) { - 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 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Debug info extension instructions other than DebugScope, " - "DebugNoScope, DebugDeclare, DebugValue must appear between " - "section 9 (types, constants, global variables) and section 10 " - "(function declarations)")); -} - -TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) { - 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" -%int_name = OpString "int" -%foo_name = OpString "foo" -)"; - - 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 -%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal -%expr = OpExtInst %void %DbgExt DebugExpression -)"; - - const std::string body = R"( -%foo = OpVariable %u32_ptr_function Function -%foo_val = OpLoad %u32 %foo -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header + GetParam(), body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " - "of debug info extension must appear in a function " - "body")); -} - -INSTANTIATE_TEST_SUITE_P( - AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction, - ::testing::ValuesIn(std::vector{ - "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info", - "%no_scope = OpExtInst %void %DbgExt DebugNoScope", - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { - 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"( -%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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -)"; - - 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, 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 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%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" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - 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 -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%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 -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info -%foo = OpVariable %f32_ptr_function Function -)"; - - 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, DebugTypeCompositeSizeDebugInfoNone) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%ty_name = OpString "struct VS_OUTPUT" -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; - float4 color : COLOR; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_color_name = OpString "color : COLOR" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - 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 %VS_OUTPUT_color_info -%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 -%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %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_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; - float4 color : COLOR; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_color_name = OpString "color : COLOR" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - 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 %VS_OUTPUT_color_info -%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 -)"; - - 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_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("forward referenced IDs have not been defined")); -} - -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, DebugTypeArrayWithVariableSize) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%int_name = OpString "int" -%main_name = OpString "main" -%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 -%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_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, 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("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -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 OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -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 OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%main_name = OpString "main" -%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 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_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("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -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_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%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 -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param -)"; - - 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, DebugTypeTemplateUsedForVariableType) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%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 -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate -)"; - - 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, DebugTypeTemplateFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%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 -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param -)"; - - 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, DebugTypeTemplateFailTarget) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%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 -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param -)"; - - 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 Target must be DebugTypeComposite or " - "DebugFunction")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%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 -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_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 Parameters must be DebugTypeTemplateParameter or " - "DebugTypeTemplateTemplateParameter")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void 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 = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate -)"; - - 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, DebugGlobalVariableStaticMember) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void 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 -%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a -%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a -)"; - - 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, DebugGlobalVariableDebugInfoNone) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void 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"( -%dbgNone = OpExtInst %void %DbgExt DebugInfoNone -%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 DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate -)"; - - 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, DebugGlobalVariableConst) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void 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 = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate -)"; - - 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(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 +%main = OpFunction %void None %func +%main_entry = OpLabel )"; - const auto& param = GetParam(); + ss << body; - 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 DebugGlobalVariable )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; +OpReturn +OpFunctionEnd)"; - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); + return ss.str(); } -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Name"), - std::make_pair( - R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Type"), - std::make_pair( - R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Source"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Scope"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)", - "Linkage Name"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)", - "Variable"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) { - 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" +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& memory_model = "Physical32") { + std::ostringstream ss; + ss << R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability GenericPointer +OpCapability Int8 +OpCapability Int16 +OpCapability Int64 +OpCapability Float16 +OpCapability Float64 +OpCapability Vector16 +OpCapability Matrix )"; - 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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at -)"; + ss << capabilities_and_extensions; + ss << "%extinst = OpExtInstImport \"OpenCL.std\"\n"; + ss << "OpMemoryModel " << memory_model << " OpenCL\n"; - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%u64 = OpTypeInt 64 0 +%u16 = OpTypeInt 16 0 +%u8 = OpTypeInt 8 0 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f32vec8 = OpTypeVector %f32 8 +%f16vec8 = OpTypeVector %f16 8 +%f32vec16 = OpTypeVector %f32 16 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%u32vec4 = OpTypeVector %u32 4 +%u32vec8 = OpTypeVector %u32 8 +%u64vec2 = OpTypeVector %u64 2 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 +%f32vec8_01010101 = OpConstantComposite %f32vec8 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1 - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) { - 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" -)"; +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 - 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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at -)"; +%u8_0 = OpConstant %u8 0 +%u8_1 = OpConstant %u8 1 +%u8_2 = OpConstant %u8 2 +%u8_3 = OpConstant %u8 3 - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; +%u16_0 = OpConstant %u16 0 +%u16_1 = OpConstant %u16 1 +%u16_2 = OpConstant %u16 2 +%u16_3 = OpConstant %u16 3 - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_256 = OpConstant %u32 256 - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); -} +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_256 = OpConstant %u64 256 -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) { - 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" -)"; +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 - 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_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info -)"; +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 +%struct_f32_u32 = OpTypeStruct %f32 %u32 +%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 +%struct_u32_f32 = OpTypeStruct %u32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); -} +%f16vec8_ptr_workgroup = OpTypePointer Workgroup %f16vec8 +%f16vec8_workgroup = OpVariable %f16vec8_ptr_workgroup Workgroup +%f16_ptr_workgroup = OpTypePointer Workgroup %f16 -TEST_F(ValidateOpenCL100DebugInfo, DebugValue) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; +%u32vec8_ptr_workgroup = OpTypePointer Workgroup %u32vec8 +%u32vec8_workgroup = OpVariable %u32vec8_ptr_workgroup Workgroup +%u32_ptr_workgroup = OpTypePointer Workgroup %u32 - const std::string size_const = R"( -%int_3 = OpConstant %u32 3 -%int_32 = OpConstant %u32 32 -)"; +%f32vec8_ptr_workgroup = OpTypePointer Workgroup %f32vec8 +%f32vec8_workgroup = OpVariable %f32vec8_ptr_workgroup Workgroup +%f32_ptr_workgroup = OpTypePointer Workgroup %f32 - 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 -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; +%u32arr = OpTypeArray %u32 %u32_256 +%u32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32arr +%u32arr_cross_workgroup = OpVariable %u32arr_ptr_cross_workgroup CrossWorkgroup +%u32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32 - const std::string body = R"( -%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3 -)"; +%f32arr = OpTypeArray %f32 %u32_256 +%f32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32arr +%f32arr_cross_workgroup = OpVariable %f32arr_ptr_cross_workgroup CrossWorkgroup +%f32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32 - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; +%f32vec2arr = OpTypeArray %f32vec2 %u32_256 +%f32vec2arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2arr +%f32vec2arr_cross_workgroup = OpVariable %f32vec2arr_ptr_cross_workgroup CrossWorkgroup +%f32vec2_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2 - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} +%struct_arr = OpTypeArray %struct_f32_f32 %u32_256 +%struct_arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_arr +%struct_arr_cross_workgroup = OpVariable %struct_arr_ptr_cross_workgroup CrossWorkgroup +%struct_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_f32_f32 -TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%int_name = OpString "int" -%foo_name = OpString "foo" -%len_name = OpString "length" -)"; +%f16vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f16vec8 +%f16vec8_uniform_constant = OpVariable %f16vec8_ptr_uniform_constant UniformConstant +%f16_ptr_uniform_constant = OpTypePointer UniformConstant %f16 - const std::string size_const = R"( -%int_3 = OpConstant %u32 3 -%int_32 = OpConstant %u32 32 -)"; +%u32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %u32vec8 +%u32vec8_uniform_constant = OpVariable %u32vec8_ptr_uniform_constant UniformConstant +%u32_ptr_uniform_constant = OpTypePointer UniformConstant %u32 - 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 -%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal -%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal -)"; +%f32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f32vec8 +%f32vec8_uniform_constant = OpVariable %f32vec8_ptr_uniform_constant UniformConstant +%f32_ptr_uniform_constant = OpTypePointer UniformConstant %f32 - const std::string body = R"( -%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info -)"; +%f16vec8_ptr_input = OpTypePointer Input %f16vec8 +%f16vec8_input = OpVariable %f16vec8_ptr_input Input +%f16_ptr_input = OpTypePointer Input %f16 - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; +%u32vec8_ptr_input = OpTypePointer Input %u32vec8 +%u32vec8_input = OpVariable %u32vec8_ptr_input Input +%u32_ptr_input = OpTypePointer Input %u32 - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} +%f32_ptr_generic = OpTypePointer Generic %f32 +%u32_ptr_generic = OpTypePointer Generic %u32 -TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; +%f32_ptr_function = OpTypePointer Function %f32 +%f32vec2_ptr_function = OpTypePointer Function %f32vec2 +%u32_ptr_function = OpTypePointer Function %u32 +%u64_ptr_function = OpTypePointer Function %u64 +%u32vec2_ptr_function = OpTypePointer Function %u32vec2 - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; +%u8arr = OpTypeArray %u8 %u32_256 +%u8arr_ptr_uniform_constant = OpTypePointer UniformConstant %u8arr +%u8arr_uniform_constant = OpVariable %u8arr_ptr_uniform_constant UniformConstant +%u8_ptr_uniform_constant = OpTypePointer UniformConstant %u8 +%u8_ptr_generic = OpTypePointer Generic %u8 - 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 +%main = OpFunction %void None %func +%main_entry = OpLabel )"; - const auto& param = GetParam(); + ss << body; - std::ostringstream ss; ss << R"( -%decl = OpExtInst %void %DbgExt DebugValue )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; +OpReturn +OpFunctionEnd)"; - 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)); + return ss.str(); } -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"), - std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"), - std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"), - })); +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_P(ValidateGlslStd450SqrtLike, IntResultType) { const std::string ext_inst_name = GetParam(); @@ -5591,7 +2805,8 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32) { "expected Interpolant to be a pointer")); } -TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) { +TEST_F(ValidateExtInst, + GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) { const std::string body = R"( %ld2 = OpLoad %f32vec2 %f32vec2_input %val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01 -- cgit v1.2.3 From 3d4246b4aaa65a4b901abd04e7d5bc1822c582bb Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Tue, 24 Aug 2021 14:45:03 -0400 Subject: Update CHANGES --- CHANGES | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 467655cc..86440584 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,30 @@ Revision history for SPIRV-Tools -v2021.3-dev 2021-06-22 - - Start v2021.3-dev +v2021.3-dev 2021-08-24 + - General + - Initial support for SPV_KHR_integer_dot_product (#4327) + - Add non-semantic vulkan extended instruction set (#4362) + - Add common enum for debug info instructions from either opencl or vulkan (#4377) + - Validator + - Add validation for SPV_EXT_shader_atomic_float16_add (#4325) + - Disallow loading a runtime-sized array (#4473) + - spirv-val: Validate vulkan debug info similarly to opencl debug info (#4466) + - Optimizer + - spirv-opt: support SPV_EXT_shader_image_int64 (#4379) + - spirv-opt: Add dataflow analysis framework (#4402) + - Add control dependence analysis to opt (#4380) + - Add spirv-opt convert-to-sampled-image pass (#4340) + - spirv-opt: Add handling of vulkan debug info to DebugInfoManager (#4423) + - Fuzz + - spirv-fuzz: support AtomicLoad (#4330) + - spirv-fuzz: Support AtomicStore (#4440) + - spirv-fuzz: TransformationWrapVectorSynonym that rewrites scalar operations using vectors (#4376) + - spirv-fuzz: Add minimal SPIR-V example to test shaders (#4415) + - spirv-fuzz: support building using gn (#4365) + - Linter + - Add new target for spirv-lint (#4446) + - spirv-lint: add basic CLI argument handling (#4478) + - Add divergence analysis to linter (#4465) v2021.2 2021-06-18 - General -- cgit v1.2.3 From 1fbed83c8aab8517d821fcb4164c08567951938f Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Wed, 25 Aug 2021 10:08:20 -0400 Subject: Finalize SPIRV-Tools v2021.3 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 86440584..44c49273 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ Revision history for SPIRV-Tools -v2021.3-dev 2021-08-24 +v2021.3 2021-08-24 - General - Initial support for SPV_KHR_integer_dot_product (#4327) - Add non-semantic vulkan extended instruction set (#4362) -- cgit v1.2.3 From 2a5cc342fb5a8166ec218963bbe5c2e5c4045c2c Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Wed, 25 Aug 2021 10:09:53 -0400 Subject: Start SPIRV-Tools v2021.4 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 44c49273..0364c25d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ Revision history for SPIRV-Tools +v2021.4-dev 2021-08-25 + - Start v2021.4-dev + v2021.3 2021-08-24 - General - Initial support for SPV_KHR_integer_dot_product (#4327) -- cgit v1.2.3 From ee30773650eca50b1cd3c913babcc2b50d7b91fd Mon Sep 17 00:00:00 2001 From: gnl21 Date: Thu, 26 Aug 2021 19:33:19 +0100 Subject: Add a feature for allowing LocalSizeId (#4492) Allow LocalSizeId as a way of sizing compute workgroups where the environment allows it. A command-line switch is also added to force acceptance even where the environment would not otherwise allow it. --- include/spirv-tools/libspirv.h | 5 +++++ include/spirv-tools/libspirv.hpp | 6 ++++++ source/spirv_validator_options.cpp | 5 +++++ source/spirv_validator_options.h | 2 ++ source/val/validate_mode_setting.cpp | 11 +++++++++++ source/val/validation_state.cpp | 3 +++ source/val/validation_state.h | 9 +++++++++ tools/val/val.cpp | 4 ++++ 8 files changed, 45 insertions(+) diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 999589e2..1cd40cc8 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -659,6 +659,11 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetWorkgroupScalarBlockLayout( SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout( spv_validator_options options, bool val); +// Records whether or not the validator should allow the LocalSizeId +// decoration where the environment otherwise would not allow it. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowLocalSizeId( + spv_validator_options options, bool val); + // Creates an optimizer options object with default options. Returns a valid // options object. The object remains valid until it is passed into // |spvOptimizerOptionsDestroy|. diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index 0c31a182..8dfb46b7 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -115,6 +115,12 @@ class ValidatorOptions { spvValidatorOptionsSetSkipBlockLayout(options_, val); } + // Enables LocalSizeId decorations where the environment would not otherwise + // allow them. + void SetAllowLocalSizeId(bool val) { + spvValidatorOptionsSetAllowLocalSizeId(options_, val); + } + // Records whether or not the validator should relax the rules on pointer // usage in logical addressing mode. // diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp index 2716cca9..e5b1eece 100644 --- a/source/spirv_validator_options.cpp +++ b/source/spirv_validator_options.cpp @@ -120,3 +120,8 @@ void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options, bool val) { options->skip_block_layout = val; } + +void spvValidatorOptionsSetAllowLocalSizeId(spv_validator_options options, + bool val) { + options->allow_localsizeid = val; +} diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h index baaa5359..a357c031 100644 --- a/source/spirv_validator_options.h +++ b/source/spirv_validator_options.h @@ -47,6 +47,7 @@ struct spv_validator_options_t { scalar_block_layout(false), workgroup_scalar_block_layout(false), skip_block_layout(false), + allow_localsizeid(false), before_hlsl_legalization(false) {} validator_universal_limits_t universal_limits_; @@ -57,6 +58,7 @@ struct spv_validator_options_t { bool scalar_block_layout; bool workgroup_scalar_block_layout; bool skip_block_layout; + bool allow_localsizeid; bool before_hlsl_legalization; }; diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index 79f82d8d..4fb6d9b9 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp @@ -225,6 +225,13 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { } } } + if (i.opcode() == SpvOpExecutionModeId) { + const auto mode = i.GetOperandAs(1); + if (mode == SpvExecutionModeLocalSizeId) { + ok = true; + break; + } + } } if (!ok) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -429,6 +436,10 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, break; case SpvExecutionModeLocalSize: case SpvExecutionModeLocalSizeId: + if (mode == SpvExecutionModeLocalSizeId && !_.IsLocalSizeIdAllowed()) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "LocalSizeId mode is not allowed by the current environment."; + if (!std::all_of(models->begin(), models->end(), [&_](const SpvExecutionModel& model) { switch (model) { diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index c9ac3ae7..e6a02dc9 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -175,6 +175,9 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, } } + // LocalSizeId is always allowed in non-Vulkan environments. + features_.env_allow_localsizeid = !spvIsVulkanEnv(env); + // Only attempt to count if we have words, otherwise let the other validation // fail and generate an error. if (num_words > 0) { diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 2fe96621..4e35cd2e 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -121,6 +121,9 @@ class ValidationState_t { // SPIR-V 1.4 allows Function and Private variables to be NonWritable bool nonwritable_var_in_function_or_private = false; + + // Whether LocalSizeId execution mode is allowed by the environment. + bool env_allow_localsizeid = false; }; ValidationState_t(const spv_const_context context, @@ -493,6 +496,12 @@ class ValidationState_t { return features_.env_relaxed_block_layout || options()->relax_block_layout; } + // Returns true if allowing localsizeid, either because the environment always + // allows it, or because it is enabled from the command-line. + bool IsLocalSizeIdAllowed() const { + return features_.env_allow_localsizeid || options()->allow_localsizeid; + } + /// Sets the struct nesting depth for a given struct ID void set_struct_nesting_depth(uint32_t id, uint32_t depth) { struct_nesting_depth_[id] = depth; diff --git a/tools/val/val.cpp b/tools/val/val.cpp index 21a7d8f4..55321dab 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp @@ -64,6 +64,8 @@ Options: --relax-struct-store Allow store from one struct type to a different type with compatible layout and members. + --allow-localsizeid Allow use of the LocalSizeId decoration where it would otherwise not + be allowed by the target environment. --before-hlsl-legalization Allows code patterns that are intended to be fixed by spirv-opt's legalization passes. --version Display validator version information. @@ -153,6 +155,8 @@ int main(int argc, char** argv) { options.SetWorkgroupScalarBlockLayout(true); } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { options.SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--allow-localsizeid")) { + options.SetAllowLocalSizeId(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { options.SetRelaxStructStore(true); } else if (0 == cur_arg[1]) { -- cgit v1.2.3 From b8fce5f9e6dabf59f8bf495229a98d53bdbf89b0 Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Fri, 27 Aug 2021 14:43:23 -0400 Subject: spirv-lint: Add lint based on divergence analysis (#4488) This PR is a rebased version of #4479 by James Dong. --- The primary purpose of this PR is to add the code from my prototype as a PR, for licensing reasons. The commit history is messy, and the code is not especially clean. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/3196. --- include/spirv-tools/linter.hpp | 2 +- source/lint/CMakeLists.txt | 2 + source/lint/lint_divergent_derivatives.cpp | 169 +++++++++++++++++++++++++++++ source/lint/linter.cpp | 21 +++- source/lint/lints.h | 34 ++++++ 5 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 source/lint/lint_divergent_derivatives.cpp create mode 100644 source/lint/lints.h diff --git a/include/spirv-tools/linter.hpp b/include/spirv-tools/linter.hpp index 57d1b4e9..52ed5a46 100644 --- a/include/spirv-tools/linter.hpp +++ b/include/spirv-tools/linter.hpp @@ -35,7 +35,7 @@ class Linter { void SetMessageConsumer(MessageConsumer consumer); // Returns a reference to the registered message consumer. - const MessageConsumer& consumer() const; + const MessageConsumer& Consumer() const; bool Run(const uint32_t* binary, size_t binary_size); diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt index f9cae28a..1feae3f9 100644 --- a/source/lint/CMakeLists.txt +++ b/source/lint/CMakeLists.txt @@ -13,9 +13,11 @@ # limitations under the License. set(SPIRV_TOOLS_LINT_SOURCES divergence_analysis.h + lints.h linter.cpp divergence_analysis.cpp + lint_divergent_derivatives.cpp ) if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) diff --git a/source/lint/lint_divergent_derivatives.cpp b/source/lint/lint_divergent_derivatives.cpp new file mode 100644 index 00000000..512847b0 --- /dev/null +++ b/source/lint/lint_divergent_derivatives.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2021 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 +#include +#include + +#include "source/diagnostic.h" +#include "source/lint/divergence_analysis.h" +#include "source/lint/lints.h" +#include "source/opt/basic_block.h" +#include "source/opt/cfg.h" +#include "source/opt/control_dependence.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace lint { +namespace lints { +namespace { +// Returns the %name[id], where `name` is the first name associated with the +// given id, or just %id if one is not found. +std::string GetFriendlyName(opt::IRContext* context, uint32_t id) { + auto names = context->GetNames(id); + std::stringstream ss; + ss << "%"; + if (names.empty()) { + ss << id; + } else { + opt::Instruction* inst_name = names.begin()->second; + if (inst_name->opcode() == SpvOpName) { + ss << names.begin()->second->GetInOperand(0).AsString(); + ss << "[" << id << "]"; + } else { + ss << id; + } + } + return ss.str(); +} + +bool InstructionHasDerivative(const opt::Instruction& inst) { + static const SpvOp derivative_opcodes[] = { + // Implicit derivatives. + SpvOpImageSampleImplicitLod, + SpvOpImageSampleDrefImplicitLod, + SpvOpImageSampleProjImplicitLod, + SpvOpImageSampleProjDrefImplicitLod, + SpvOpImageSparseSampleImplicitLod, + SpvOpImageSparseSampleDrefImplicitLod, + SpvOpImageSparseSampleProjImplicitLod, + SpvOpImageSparseSampleProjDrefImplicitLod, + // Explicit derivatives. + SpvOpDPdx, + SpvOpDPdy, + SpvOpFwidth, + SpvOpDPdxFine, + SpvOpDPdyFine, + SpvOpFwidthFine, + SpvOpDPdxCoarse, + SpvOpDPdyCoarse, + SpvOpFwidthCoarse, + }; + return std::find(std::begin(derivative_opcodes), std::end(derivative_opcodes), + inst.opcode()) != std::end(derivative_opcodes); +} + +spvtools::DiagnosticStream Warn(opt::IRContext* context, + opt::Instruction* inst) { + if (inst == nullptr) { + return DiagnosticStream({0, 0, 0}, context->consumer(), "", SPV_WARNING); + } else { + // TODO(kuhar): Use line numbers based on debug info. + return DiagnosticStream( + {0, 0, 0}, context->consumer(), + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES), + SPV_WARNING); + } +} + +void PrintDivergenceFlow(opt::IRContext* context, DivergenceAnalysis div, + uint32_t id) { + opt::analysis::DefUseManager* def_use = context->get_def_use_mgr(); + opt::CFG* cfg = context->cfg(); + while (id != 0) { + bool is_block = def_use->GetDef(id)->opcode() == SpvOpLabel; + if (is_block) { + Warn(context, nullptr) + << "block " << GetFriendlyName(context, id) << " is divergent"; + uint32_t source = div.GetDivergenceSource(id); + // Skip intermediate blocks. + while (source != 0 && def_use->GetDef(source)->opcode() == SpvOpLabel) { + id = source; + source = div.GetDivergenceSource(id); + } + if (source == 0) break; + spvtools::opt::Instruction* branch = + cfg->block(div.GetDivergenceDependenceSource(id))->terminator(); + Warn(context, branch) + << "because it depends on a conditional branch on divergent value " + << GetFriendlyName(context, source) << ""; + id = source; + } else { + Warn(context, nullptr) + << "value " << GetFriendlyName(context, id) << " is divergent"; + uint32_t source = div.GetDivergenceSource(id); + opt::Instruction* def = def_use->GetDef(id); + opt::Instruction* source_def = + source == 0 ? nullptr : def_use->GetDef(source); + // First print data -> data dependencies. + while (source != 0 && source_def->opcode() != SpvOpLabel) { + Warn(context, def_use->GetDef(id)) + << "because " << GetFriendlyName(context, id) << " uses value " + << GetFriendlyName(context, source) + << "in its definition, which is divergent"; + id = source; + def = source_def; + source = div.GetDivergenceSource(id); + source_def = def_use->GetDef(source); + } + if (source == 0) { + Warn(context, def) << "because it has a divergent definition"; + break; + } + Warn(context, def) << "because it is conditionally set in block " + << GetFriendlyName(context, source); + id = source; + } + } +} +} // namespace + +bool CheckDivergentDerivatives(opt::IRContext* context) { + DivergenceAnalysis div(*context); + for (opt::Function& func : *context->module()) { + div.Run(&func); + for (const opt::BasicBlock& bb : func) { + for (const opt::Instruction& inst : bb) { + if (InstructionHasDerivative(inst) && + div.GetDivergenceLevel(bb.id()) > + DivergenceAnalysis::DivergenceLevel::kPartiallyUniform) { + Warn(context, nullptr) + << "derivative with divergent control flow" + << " located in block " << GetFriendlyName(context, bb.id()); + PrintDivergenceFlow(context, div, bb.id()); + } + } + } + } + return true; +} + +} // namespace lints +} // namespace lint +} // namespace spvtools diff --git a/source/lint/linter.cpp b/source/lint/linter.cpp index 0f847953..e4ed04ea 100644 --- a/source/lint/linter.cpp +++ b/source/lint/linter.cpp @@ -14,6 +14,13 @@ #include "spirv-tools/linter.hpp" +#include "source/lint/lints.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv/unified1/spirv.h" + namespace spvtools { struct Linter::Impl { @@ -32,20 +39,22 @@ Linter::Linter(spv_target_env env) : impl_(new Impl(env)) {} Linter::~Linter() {} void Linter::SetMessageConsumer(MessageConsumer consumer) { - impl_->message_consumer = consumer; + impl_->message_consumer = std::move(consumer); } -const MessageConsumer& Linter::consumer() const { +const MessageConsumer& Linter::Consumer() const { return impl_->message_consumer; } bool Linter::Run(const uint32_t* binary, size_t binary_size) { - (void)binary; - (void)binary_size; + std::unique_ptr context = + BuildModule(SPV_ENV_VULKAN_1_2, Consumer(), binary, binary_size); + if (context == nullptr) return false; - consumer()(SPV_MSG_INFO, "", {0, 0, 0}, "Hello, world!"); + bool result = true; + result &= lint::lints::CheckDivergentDerivatives(context.get()); - return true; + return result; } } // namespace spvtools diff --git a/source/lint/lints.h b/source/lint/lints.h new file mode 100644 index 00000000..a1995d2f --- /dev/null +++ b/source/lint/lints.h @@ -0,0 +1,34 @@ +// Copyright (c) 2021 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_LINT_LINTS_H_ +#define SOURCE_LINT_LINTS_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace lint { + +// All of the functions in this namespace output to the error consumer in the +// |context| argument and return |true| if no errors are found. They do not +// modify the IR. +namespace lints { + +bool CheckDivergentDerivatives(opt::IRContext* context); + +} // namespace lints +} // namespace lint +} // namespace spvtools + +#endif // SOURCE_LINT_LINTS_H_ -- cgit v1.2.3 From 702e6af3800bb1799a66129c5c12c32dd86549b0 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Mon, 30 Aug 2021 09:39:40 -0400 Subject: Only add `-DSPIRV_CHECK_CONTEXT` for Debug builds (#4496) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12231724..05c1876b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,7 +294,7 @@ endif() # Turn off if they take too long. option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON) if (${SPIRV_CHECK_CONTEXT}) - add_definitions(-DSPIRV_CHECK_CONTEXT) + add_compile_options($<$:-DSPIRV_CHECK_CONTEXT>) endif() # Precompiled header macro. Parameters are source file list and filename for pch cpp file. -- cgit v1.2.3 From bd3a271ce3fcc8b9ed8e6eb2e94392d1b220adc9 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 31 Aug 2021 12:39:46 -0400 Subject: Handle exported functions in ADCE (#4495) ADCE does not handle exported functions. This was an explicit decision because we did not believe that the linkage attribute could be used in shaders, but it can now. This change has been made. While fixing this error, I noticed that the OpName for labels is sometimes removed because the label instructions are not marked explicitly marked as live. This has able been fixed. --- source/opt/aggressive_dead_code_elim_pass.cpp | 14 ++-- test/opt/aggressive_dead_code_elim_test.cpp | 105 ++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 90d30e9a..a55e221a 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -440,6 +440,11 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { if (liveInst->type_id() != 0) { AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id())); } + BasicBlock* basic_block = context()->get_instr_block(liveInst); + if (basic_block != nullptr) { + AddToWorklist(basic_block->GetLabelInst()); + } + // If in a structured if or loop construct, add the controlling // conditional branch and its merge. BasicBlock* blk = context()->get_instr_block(liveInst); @@ -690,7 +695,7 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { // Process all entry point functions. ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); }; - modified |= context()->ProcessEntryPointCallTree(pfn); + modified |= context()->ProcessReachableCallTree(pfn); // If the decoration manager is kept live then the context will try to keep it // up to date. ADCE deals with group decorations by changing the operands in @@ -717,21 +722,20 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { // Cleanup all CFG including all unreachable blocks. ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); }; - modified |= context()->ProcessEntryPointCallTree(cleanup); + modified |= context()->ProcessReachableCallTree(cleanup); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } bool AggressiveDCEPass::EliminateDeadFunctions() { // Identify live functions first. Those that are not live - // are dead. ADCE is disabled for non-shaders so we do not check for exported - // functions here. + // are dead. std::unordered_set live_function_set; ProcessFunction mark_live = [&live_function_set](Function* fp) { live_function_set.insert(fp); return false; }; - context()->ProcessEntryPointCallTree(mark_live); + context()->ProcessReachableCallTree(mark_live); bool modified = false; for (auto funcIter = get_module()->begin(); diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 5b4291da..d2581ead 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -7584,6 +7584,111 @@ TEST_F(AggressiveDCETest, KeepDebugScopeParent) { SinglePassRunAndMatch(text, true); } +TEST_F(AggressiveDCETest, KeepExportFunctions) { + // All functions are reachable. In particular, ExportedFunc and Constant are + // reachable because ExportedFunc is exported. Nothing should be removed. + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Linkage", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %ExportedFunc \"ExportedFunc\"", + "OpName %Live \"Live\"", + "OpDecorate %ExportedFunc LinkageAttributes \"ExportedFunc\" Export", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "OpReturn", + "OpFunctionEnd", +"%ExportedFunc = OpFunction %void None %7", + "%19 = OpLabel", + "%16 = OpFunctionCall %void %Live", + "OpReturn", + "OpFunctionEnd", + "%Live = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + std::string assembly = JoinAllInsts(text); + auto result = SinglePassRunAndDisassemble( + assembly, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(assembly, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, KeepPrivateVarInExportFunctions) { + // The loads and stores from the private variable should not be removed + // because the functions are exported and could be called. + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %privateVar "privateVar" +OpName %ReadPrivate "ReadPrivate" +OpName %WritePrivate "WritePrivate" +OpName %value "value" +OpDecorate %ReadPrivate LinkageAttributes "ReadPrivate" Export +OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export +%int = OpTypeInt 32 1 +%_ptr_Private_int = OpTypePointer Private %int +%6 = OpTypeFunction %int +%void = OpTypeVoid +%_ptr_Function_int = OpTypePointer Function %int +%10 = OpTypeFunction %void %_ptr_Function_int +%privateVar = OpVariable %_ptr_Private_int Private +%ReadPrivate = OpFunction %int None %6 +%12 = OpLabel +%8 = OpLoad %int %privateVar +OpReturnValue %8 +OpFunctionEnd +%WritePrivate = OpFunction %void None %10 +%value = OpFunctionParameter %_ptr_Function_int +%13 = OpLabel +%14 = OpLoad %int %value +OpStore %privateVar %14 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, KeepLableNames) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %WritePrivate "WritePrivate" +OpName %entry "entry" +OpName %target "target" +OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export +%void = OpTypeVoid +%3 = OpTypeFunction %void +%WritePrivate = OpFunction %void None %3 +%entry = OpLabel +OpBranch %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} // TODO(greg-lunarg): Add tests to verify handling of these cases: // // Check that logical addressing required -- cgit v1.2.3 From 0c09258e073bdbae19d2de8386125b35b783928b Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Thu, 2 Sep 2021 10:45:51 -0400 Subject: Set threshold for reduce-load-size pass (#4499) Allow uses to set the threshold for spirv-opt reduce-load-size pass --- include/spirv-tools/optimizer.hpp | 7 ++- source/opt/optimizer.cpp | 23 ++++++++-- source/opt/reduce_load_size.cpp | 5 ++- source/opt/reduce_load_size.h | 8 ++++ test/opt/reduce_load_size_test.cpp | 89 +++++++++++++++++++++++++++++++++++--- tools/opt/opt.cpp | 7 ++- 6 files changed, 125 insertions(+), 14 deletions(-) diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index b1442dcf..8141ec86 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -701,8 +701,11 @@ Optimizer::PassToken CreateVectorDCEPass(); // Create a pass to reduce the size of loads. // This pass looks for loads of structures where only a few of its members are // used. It replaces the loads feeding an OpExtract with an OpAccessChain and -// a load of the specific elements. -Optimizer::PassToken CreateReduceLoadSizePass(); +// a load of the specific elements. The parameter is a threshold to determine +// whether we have to replace the load or not. If the ratio of the used +// components of the load is less than the threshold, we replace the load. +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold = 0.9); // Create a pass to combine chained access chains. // This pass looks for access chains fed by other access chains and combines diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 858c95ee..32f953bb 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -385,7 +385,23 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "loop-invariant-code-motion") { RegisterPass(CreateLoopInvariantCodeMotionPass()); } else if (pass_name == "reduce-load-size") { - RegisterPass(CreateReduceLoadSizePass()); + if (pass_args.size() == 0) { + RegisterPass(CreateReduceLoadSizePass()); + } else { + double load_replacement_threshold = 0.9; + if (pass_args.find_first_not_of(".0123456789") == std::string::npos) { + load_replacement_threshold = atof(pass_args.c_str()); + } + + if (load_replacement_threshold >= 0) { + RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold)); + } else { + Error(consumer(), nullptr, {}, + "--reduce-load-size must have no arguments or a non-negative " + "double argument"); + return false; + } + } } else if (pass_name == "redundancy-elimination") { RegisterPass(CreateRedundancyEliminationPass()); } else if (pass_name == "private-to-local") { @@ -879,9 +895,10 @@ Optimizer::PassToken CreateVectorDCEPass() { return MakeUnique(MakeUnique()); } -Optimizer::PassToken CreateReduceLoadSizePass() { +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold) { return MakeUnique( - MakeUnique()); + MakeUnique(load_replacement_threshold)); } Optimizer::PassToken CreateCombineAccessChainsPass() { diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp index 87d2c4c0..e9b80874 100644 --- a/source/opt/reduce_load_size.cpp +++ b/source/opt/reduce_load_size.cpp @@ -27,7 +27,6 @@ namespace { const uint32_t kExtractCompositeIdInIdx = 0; const uint32_t kVariableStorageClassInIdx = 0; const uint32_t kLoadPointerInIdx = 0; -const double kThreshold = 0.9; } // namespace @@ -151,6 +150,8 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { bool should_replace = false; if (all_elements_used) { should_replace = false; + } else if (1.0 <= replacement_threshold_) { + should_replace = true; } else { analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -172,7 +173,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { } double percent_used = static_cast(elements_used.size()) / static_cast(total_size); - should_replace = (percent_used < kThreshold); + should_replace = (percent_used < replacement_threshold_); } should_replace_cache_[op_inst->result_id()] = should_replace; diff --git a/source/opt/reduce_load_size.h b/source/opt/reduce_load_size.h index ccac49be..b3238453 100644 --- a/source/opt/reduce_load_size.h +++ b/source/opt/reduce_load_size.h @@ -27,6 +27,9 @@ namespace opt { // See optimizer.hpp for documentation. class ReduceLoadSize : public Pass { public: + explicit ReduceLoadSize(double replacement_threshold) + : replacement_threshold_(replacement_threshold) {} + const char* name() const override { return "reduce-load-size"; } Status Process() override; @@ -54,6 +57,11 @@ class ReduceLoadSize : public Pass { // on the load feeding |inst|. bool ShouldReplaceExtract(Instruction* inst); + // Threshold to determine whether we have to replace the load or not. If the + // ratio of the used components of the load is less than the threshold, we + // replace the load. + double replacement_threshold_; + // Maps the result id of an OpLoad instruction to the result of whether or // not the OpCompositeExtract that use the id should be replaced. std::unordered_map should_replace_cache_; diff --git a/test/opt/reduce_load_size_test.cpp b/test/opt/reduce_load_size_test.cpp index 7672e8f3..abb5cde6 100644 --- a/test/opt/reduce_load_size_test.cpp +++ b/test/opt/reduce_load_size_test.cpp @@ -17,6 +17,12 @@ #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" +namespace { + +const double kDefaultLoadReductionThreshold = 0.9; + +} // namespace + namespace spvtools { namespace opt { namespace { @@ -104,7 +110,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract) { SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndMatch(test, false); + SinglePassRunAndMatch(test, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) { @@ -202,7 +209,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) { SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndMatch(test, false); + SinglePassRunAndMatch(test, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) { @@ -280,7 +288,8 @@ OpFunctionEnd SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_5_extract) { @@ -351,7 +360,8 @@ OpFunctionEnd SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_fully_used) { @@ -416,7 +426,76 @@ OpFunctionEnd SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); +} + +TEST_F(ReduceLoadSizeTest, replace_cbuffer_load_fully_used) { + const std::string test = + R"( + OpCapability Shader + OpCapability SampledBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target0 + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_MaterialInstancing_cbuffer "type.MaterialInstancing_cbuffer" + OpMemberName %type_MaterialInstancing_cbuffer 0 "MaterialInstancing_constants" + OpName %MaterialInstancing_Constants "MaterialInstancing_Constants" + OpMemberName %MaterialInstancing_Constants 0 "offset0" + OpMemberName %MaterialInstancing_Constants 1 "params" + OpName %InstancingParams_Constants "InstancingParams_Constants" + OpMemberName %InstancingParams_Constants 0 "offset1" + OpName %MaterialInstancing_cbuffer "MaterialInstancing_cbuffer" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %main "main" + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %MaterialInstancing_cbuffer DescriptorSet 6 + OpDecorate %MaterialInstancing_cbuffer Binding 0 + OpMemberDecorate %InstancingParams_Constants 0 Offset 0 + OpMemberDecorate %MaterialInstancing_Constants 0 Offset 0 + OpMemberDecorate %MaterialInstancing_Constants 1 Offset 16 + OpMemberDecorate %type_MaterialInstancing_cbuffer 0 Offset 0 + OpDecorate %type_MaterialInstancing_cbuffer Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v4int = OpTypeVector %int 4 +%InstancingParams_Constants = OpTypeStruct %v4int +%MaterialInstancing_Constants = OpTypeStruct %v4int %InstancingParams_Constants +%type_MaterialInstancing_cbuffer = OpTypeStruct %MaterialInstancing_Constants +%_ptr_Uniform_type_MaterialInstancing_cbuffer = OpTypePointer Uniform %type_MaterialInstancing_cbuffer +%_ptr_Output_int = OpTypePointer Output %int + %void = OpTypeVoid + %60 = OpTypeFunction %void +%_ptr_Uniform_MaterialInstancing_Constants = OpTypePointer Uniform %MaterialInstancing_Constants +%MaterialInstancing_cbuffer = OpVariable %_ptr_Uniform_type_MaterialInstancing_cbuffer Uniform +%out_var_SV_Target0 = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %60 + %80 = OpLabel + %131 = OpAccessChain %_ptr_Uniform_MaterialInstancing_Constants %MaterialInstancing_cbuffer %int_0 + %132 = OpLoad %MaterialInstancing_Constants %131 +; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %MaterialInstancing_cbuffer %int_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_0 +; CHECK: OpLoad %v4int [[ac2]] + +; CHECK: [[ac3:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1 +; CHECK: [[ac4:%\w+]] = OpAccessChain {{%\w+}} [[ac3]] %uint_0 +; CHECK: OpLoad %v4int [[ac4]] + %134 = OpCompositeExtract %v4int %132 0 + %135 = OpCompositeExtract %InstancingParams_Constants %132 1 + %136 = OpCompositeExtract %v4int %135 0 + %149 = OpCompositeExtract %int %134 0 + %185 = OpCompositeExtract %int %136 0 + %156 = OpIAdd %int %149 %185 + OpStore %out_var_SV_Target0 %156 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(test, false, 1.1); } } // namespace diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 28770cb9..d5036fc3 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -387,9 +387,12 @@ Options (in lexicographical order):)", Change the scope of private variables that are used in a single function to that function.)"); printf(R"( - --reduce-load-size + --reduce-load-size[=] Replaces loads of composite objects where not every component is - used by loads of just the elements that are used.)"); + used by loads of just the elements that are used. If the ratio + of the used components of the load is less than the , + we replace the load. is a double type number. If + it is bigger than 1.0, we always replaces the load.)"); printf(R"( --redundancy-elimination Looks for instructions in the same function that compute the -- cgit v1.2.3 From 789de0dc4bc952c8b21c5d6c5ed5cc8656b1e135 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Sep 2021 23:55:30 +0100 Subject: Adjust build for fuzzer targets to support OSS-Fuzz (#4498) With OSS-Fuzz, the build system should not directly set options such as -fsanitize=fuzzer. Instead, these are set by OSS-Fuzz, and linker options are provided via the LIB_FUZZER_OPTIONS environment variable. This change allows the fuzzers to be build stand-alone, outside of OSS-Fuzz, in the way that was already supported, as well as inside OSS-Fuzz, when the LIB_FUZZER_OPTIONS environment variable is set. --- CMakeLists.txt | 2 ++ test/fuzzers/CMakeLists.txt | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05c1876b..01e3b16f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,8 @@ endif(SPIRV_BUILD_COMPRESSION) option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF) +set(SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to control, via link options, which fuzzing engine should be used") + option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF) option(SPIRV_WERROR "Enable error on warning" ON) diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt index 6b0926ea..afcc6b8f 100644 --- a/test/fuzzers/CMakeLists.txt +++ b/test/fuzzers/CMakeLists.txt @@ -26,8 +26,17 @@ function(add_spvtools_libfuzzer_target) ${spirv-tools_BINARY_DIR} ) set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools libFuzzer targets") - target_compile_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") - target_link_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + if(NOT ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS} STREQUAL "") + # This is set when the fuzzers are being built by OSS-Fuzz. In this case the + # variable provides the necessary linker flags, and OSS-Fuzz will take care + # of passing suitable compiler flags. + target_link_options(${ARG_TARGET} PRIVATE ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS}) + else() + # When the fuzzers are being built outside of OSS-Fuzz, standard libFuzzer + # arguments to enable fuzzing are used. + target_compile_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + target_link_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + endif() endfunction() if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) -- cgit v1.2.3 From 7e860e3831439fe7f1e601766d3ad8374aa8a78c Mon Sep 17 00:00:00 2001 From: David Neto Date: Fri, 3 Sep 2021 12:27:12 -0400 Subject: fix parsing of bad binary exponents in hex floats (#4501) - The binary exponent must have some decimal digits - A + or - after the binary exponent digits should not be interpreted as part of the binary exponent. Fixes: #4500 --- source/util/hex_float.h | 16 ++++++++++- test/hex_float_test.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/source/util/hex_float.h b/source/util/hex_float.h index cfc40fa6..be28eae3 100644 --- a/source/util/hex_float.h +++ b/source/util/hex_float.h @@ -1005,6 +1005,9 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { is.get(); next_char = is.peek(); } + + // Finished reading the part preceding any '.' or 'p'. + bits_written = false; while (seen_dot && !seen_p) { // Handle only fractional parts now. @@ -1037,11 +1040,16 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { next_char = is.peek(); } + // Finished reading the part preceding 'p'. + // In hex floats syntax, the binary exponent is required. + bool seen_sign = false; int8_t exponent_sign = 1; + bool seen_written_exponent_digits = false; int_type written_exponent = 0; while (true) { - if ((next_char == '-' || next_char == '+')) { + if (!seen_written_exponent_digits && + (next_char == '-' || next_char == '+')) { if (seen_sign) { is.setstate(std::ios::failbit); return is; @@ -1049,6 +1057,7 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { seen_sign = true; exponent_sign = (next_char == '-') ? -1 : 1; } else if (::isdigit(next_char)) { + seen_written_exponent_digits = true; // Hex-floats express their exponent as decimal. written_exponent = static_cast(written_exponent * 10); written_exponent = @@ -1059,6 +1068,11 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { is.get(); next_char = is.peek(); } + if (!seen_written_exponent_digits) { + // Binary exponent had no digits. + is.setstate(std::ios::failbit); + return is; + } written_exponent = static_cast(written_exponent * exponent_sign); exponent = static_cast(exponent + written_exponent); diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp index c422f756..ffdb8bda 100644 --- a/test/hex_float_test.cpp +++ b/test/hex_float_test.cpp @@ -1325,6 +1325,76 @@ TEST(FloatProxy, Lowest) { Eq(std::numeric_limits::lowest())); } +template +struct StreamParseCase { + StreamParseCase(const std::string& lit, bool succ, const std::string& suffix, + T value) + : literal(lit), + expect_success(succ), + expected_suffix(suffix), + expected_value(HexFloat>(value)) {} + + std::string literal; + bool expect_success; + std::string expected_suffix; + HexFloat> expected_value; +}; + +template +std::ostream& operator<<(std::ostream& os, const StreamParseCase& fspc) { + os << "StreamParseCase(" << fspc.literal + << ", expect_succes:" << int(fspc.expect_success) << "," + << fspc.expected_suffix << "," << fspc.expected_value << ")"; + return os; +} + +using FloatStreamParseTest = ::testing::TestWithParam>; + +TEST_P(FloatStreamParseTest, Samples) { + std::stringstream input(GetParam().literal); + HexFloat> parsed_value(0.0f); + // Hex floats must be read with the stream input operator. + input >> parsed_value; + if (GetParam().expect_success) { + EXPECT_FALSE(input.fail()); + std::string suffix; + input >> suffix; + // EXPECT_EQ(suffix, GetParam().expected_suffix); + EXPECT_EQ(parsed_value.value().getAsFloat(), + GetParam().expected_value.value().getAsFloat()); + } else { + EXPECT_TRUE(input.fail()); + } +} + +INSTANTIATE_TEST_SUITE_P( + HexFloatExponentMissingDigits, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + {"0x1.0p1", true, "", 2.0f}, + {"0x1.0p1a", true, "a", 2.0f}, + {"-0x1.0p1f", true, "f", -2.0f}, + {"0x1.0p", false, "", 0.0f}, + {"0x1.0pa", false, "", 0.0f}, + {"0x1.0p!", false, "", 0.0f}, + {"0x1.0p+", false, "", 0.0f}, + {"0x1.0p+a", false, "", 0.0f}, + {"0x1.0p+!", false, "", 0.0f}, + {"0x1.0p-", false, "", 0.0f}, + {"0x1.0p-a", false, "", 0.0f}, + {"0x1.0p-!", false, "", 0.0f}, + {"0x1.0p++", false, "", 0.0f}, + {"0x1.0p+-", false, "", 0.0f}, + {"0x1.0p-+", false, "", 0.0f}, + {"0x1.0p--", false, "", 0.0f}})); + +INSTANTIATE_TEST_SUITE_P( + HexFloatExponentTrailingSign, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + // Don't consume a sign after the binary exponent digits. + {"0x1.0p1", true, "", 2.0f}, + {"0x1.0p1+", true, "+", 2.0f}, + {"0x1.0p1-", true, "-", 2.0f}})); + // TODO(awoloszyn): Add fp16 tests and HexFloatTraits. } // namespace } // namespace utils -- cgit v1.2.3 From 4db6b8dcc2f50d20c2d190395e15e01613a33b87 Mon Sep 17 00:00:00 2001 From: gnl21 Date: Tue, 7 Sep 2021 18:08:15 +0100 Subject: Remove environment features that are never used (#4491) The validation state contained feature bits for scalar block layout and workgroup memory scalar block layout which were never used (the command-line option is used in every case). --- source/val/validation_state.h | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 4e35cd2e..2ddfa4a9 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -90,23 +90,6 @@ class ValidationState_t { // conversion opcodes bool use_int8_type = false; - // Use scalar block layout. See VK_EXT_scalar_block_layout: - // Defines scalar alignment: - // - scalar alignment equals the scalar size in bytes - // - array alignment is same as its element alignment - // - array alignment is max alignment of any of its members - // - vector alignment is same as component alignment - // - matrix alignment is same as component alignment - // For struct in Uniform, StorageBuffer, PushConstant: - // - Offset of a member is multiple of scalar alignment of that member - // - ArrayStride and MatrixStride are multiples of scalar alignment - // Members need not be listed in offset order - bool scalar_block_layout = false; - - // Use scalar block layout (as defined above) for Workgroup block - // variables. See VK_KHR_workgroup_memory_explicit_layout. - bool workgroup_scalar_block_layout = false; - // SPIR-V 1.4 allows us to select between any two composite values // of the same type. bool select_between_composites = false; -- cgit v1.2.3 From 92868b8f3f24b71a924a98f1af71c02b04b5ce45 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 7 Sep 2021 20:37:40 +0100 Subject: spirv-val: Fix ubsan error (#4505) Fixes an issue where an arbitrary word was cast to SpvOp, leading to undefined behaviour if the value of the word fell outside the range of SpvOp values. Fixes #4504. --- source/binary.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/binary.cpp b/source/binary.cpp index 090cccfe..93d5da7a 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -507,7 +507,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { assert(SpvOpSpecConstantOp == opcode); - if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) { + if (word > static_cast(SpvOp::SpvOpMax) || + grammar_.lookupSpecConstantOpcode(SpvOp(word))) { return diagnostic() << "Invalid " << spvOperandTypeStr(type) << ": " << word; } -- cgit v1.2.3 From c16224c6845e8b7243db8c8a6a70af2bf9abae74 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 8 Sep 2021 05:09:01 -0400 Subject: Add some missing switch validation (#4507) Fixes #4506 * Add validation of selector and default operands --- source/val/validate_cfg.cpp | 12 +++++++++++ test/val/val_cfg_test.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index 36f632a8..f08ab19f 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -199,6 +199,18 @@ spv_result_t ValidateSwitch(ValidationState_t& _, const Instruction* inst) { // At least two operands (selector, default), any more than that are // literal/target. + const auto sel_type_id = _.GetOperandTypeId(inst, 0); + if (!_.IsIntScalarType(sel_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Selector type must be OpTypeInt"; + } + + const auto default_label = _.FindDef(inst->GetOperandAs(1)); + if (default_label->opcode() != SpvOpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Default must be an OpLabel instruction"; + } + // target operands must be OpLabel for (size_t i = 2; i < num_operands; i += 2) { // literal, id diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 6ae2ee6d..a11b625c 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -4399,6 +4399,56 @@ OpFunctionEnd "1[%BAD], but not via a structured exit")); } +TEST_F(ValidateCFG, SwitchSelectorNotAnInt) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %default None +OpSwitch %float_1 %default +%default = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Selector type must be OpTypeInt")); +} + +TEST_F(ValidateCFG, SwitchDefaultNotALabel) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %default None +OpSwitch %int_1 %int_1 +%default = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Default must be an OpLabel instruction")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 2a938fcfa3ff19f0446be4da213914a75c6606c5 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Fri, 10 Sep 2021 13:53:09 +0100 Subject: Add UBSan kokoro configuration (#4512) Adds a kokoro configuration for UBSan, to allow this sanitizer to be used during continuous integration. --- kokoro/linux-clang-ubsan/build.sh | 24 ++++++++++++++++++++++++ kokoro/linux-clang-ubsan/continuous.cfg | 16 ++++++++++++++++ kokoro/linux-clang-ubsan/presubmit.cfg | 16 ++++++++++++++++ kokoro/scripts/linux/build-docker.sh | 9 ++++++++- 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100755 kokoro/linux-clang-ubsan/build.sh create mode 100644 kokoro/linux-clang-ubsan/continuous.cfg create mode 100644 kokoro/linux-clang-ubsan/presubmit.cfg diff --git a/kokoro/linux-clang-ubsan/build.sh b/kokoro/linux-clang-ubsan/build.sh new file mode 100755 index 00000000..b5941e34 --- /dev/null +++ b/kokoro/linux-clang-ubsan/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2021 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. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh UBSAN clang cmake diff --git a/kokoro/linux-clang-ubsan/continuous.cfg b/kokoro/linux-clang-ubsan/continuous.cfg new file mode 100644 index 00000000..cb5535e1 --- /dev/null +++ b/kokoro/linux-clang-ubsan/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2021 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. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh" diff --git a/kokoro/linux-clang-ubsan/presubmit.cfg b/kokoro/linux-clang-ubsan/presubmit.cfg new file mode 100644 index 00000000..029c74a5 --- /dev/null +++ b/kokoro/linux-clang-ubsan/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2021 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. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh" diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh index c6bbe951..84457a1b 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh @@ -58,7 +58,7 @@ if [ $TOOL = "cmake" ]; then using ninja-1.10.0 # Possible configurations are: - # ASAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW + # ASAN, UBSAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW BUILD_TYPE="Debug" if [ $CONFIG = "RELEASE" ] || [ $CONFIG = "RELEASE_MINGW" ]; then BUILD_TYPE="RelWithDebInfo" @@ -69,6 +69,13 @@ if [ $TOOL = "cmake" ]; then if [ $CONFIG = "ASAN" ]; then ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=address,bounds,null" [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } + elif [ $CONFIG = "UBSAN" ]; then + # UBSan requires RTTI, and by default UBSan does not exit when errors are + # encountered - additional compiler options are required to force this. + # The -DSPIRV_USE_SANITIZER=undefined option instructs SPIR-V Tools to be + # built with UBSan enabled. + ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=undefined -DENABLE_RTTI=ON -DCMAKE_C_FLAGS=-fno-sanitize-recover -DCMAKE_CXX_FLAGS=-fno-sanitize-recover" + [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } elif [ $CONFIG = "COVERAGE" ]; then ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON" SKIP_TESTS="True" -- cgit v1.2.3 From 013b1f3d6d9cf9eee14a50a5cfe50b5e9300043e Mon Sep 17 00:00:00 2001 From: David Neto Date: Fri, 10 Sep 2021 11:28:00 -0400 Subject: Fix validation message for cooperative matrix column type (#4502) * Fix validation message for cooperative matrix column type Fixes: #4497 * Add tests for cooperative matrix type validation --- source/val/validate_type.cpp | 2 +- test/val/val_arithmetics_test.cpp | 51 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp index 612fc5c2..4376b52c 100644 --- a/source/val/validate_type.cpp +++ b/source/val/validate_type.cpp @@ -596,7 +596,7 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, if (!cols || !_.IsIntScalarType(cols->type_id()) || !spvOpcodeIsConstant(cols->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Cols '" << _.getIdName(rows_id) + << "OpTypeCooperativeMatrixNV Cols '" << _.getIdName(cols_id) << "' is not a constant instruction with scalar integer type."; } diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp index b82fc97e..856ad02c 100644 --- a/test/val/val_arithmetics_test.cpp +++ b/test/val/val_arithmetics_test.cpp @@ -1309,6 +1309,57 @@ TEST_F(ValidateArithmetics, CoopMatDimFail) { HasSubstr("Cooperative matrix 'M' mismatch: CooperativeMatrixMulAddNV")); } +TEST_F(ValidateArithmetics, CoopMatComponentTypeNotScalarNumeric) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %bool %subgroup %u32_8 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Component Type " + "'4[%bool]' is not a scalar numerical type.")); +} + +TEST_F(ValidateArithmetics, CoopMatScopeNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %f32_1 %u32_8 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Scope '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + +TEST_F(ValidateArithmetics, CoopMatRowsNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %f32_1 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Rows '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + +TEST_F(ValidateArithmetics, CoopMatColumnsNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %f32_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Cols '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + TEST_F(ValidateArithmetics, IAddCarrySuccess) { const std::string body = R"( %val1 = OpIAddCarry %struct_u32_u32 %u32_0 %u32_1 -- cgit v1.2.3 From 912460e46ae85fc9a44e19c59447484bb6e43541 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Fri, 10 Sep 2021 23:27:14 -0400 Subject: Fix infinite loop in GetBlockDepth (#4519) Fixes #4515 * Sets block depth to 0 if it encountered multiple times and leaves finding the real error for other code --- source/val/function.cpp | 3 +++ test/val/val_cfg_test.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/source/val/function.cpp b/source/val/function.cpp index 249c8664..9ad68e86 100644 --- a/source/val/function.cpp +++ b/source/val/function.cpp @@ -308,6 +308,9 @@ int Function::GetBlockDepth(BasicBlock* bb) { if (block_depth_.find(bb) != block_depth_.end()) { return block_depth_[bb]; } + // Avoid recursion. Something is wrong if the same block is encountered + // multiple times. + block_depth_[bb] = 0; BasicBlock* bb_dom = bb->immediate_dominator(); if (!bb_dom || bb == bb_dom) { diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index a11b625c..dd57f018 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -4449,6 +4449,39 @@ OpFunctionEnd HasSubstr("Default must be an OpLabel instruction")); } +TEST_F(ValidateCFG, BlockDepthRecursion) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef %3 %4 +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpBranch %5 +%5 = OpLabel +OpSelectionMerge %2 None +OpBranchConditional %undef %6 %7 +%6 = OpLabel +OpReturn +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 4f4f76037cf0b25a65eef1b6e3e550f3e4bf00cd Mon Sep 17 00:00:00 2001 From: alan-baker Date: Sun, 12 Sep 2021 19:14:33 -0400 Subject: Change validator boolean tests to avoid asserts (#4503) Fixes https://crbug.com/38102 * Check for valid instructions in many boolean tests instead of asserting in ValidationState_t --- source/val/validation_state.cpp | 60 ++++++++++++++++++++++++----------------- test/val/val_ext_inst_test.cpp | 13 +++++++++ 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index e6a02dc9..10ebd68c 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -732,19 +732,19 @@ uint32_t ValidationState_t::GetBitWidth(uint32_t id) const { bool ValidationState_t::IsVoidType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeVoid; + return inst && inst->opcode() == SpvOpTypeVoid; } bool ValidationState_t::IsFloatScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeFloat; + return inst && inst->opcode() == SpvOpTypeFloat; } bool ValidationState_t::IsFloatVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsFloatScalarType(GetComponentType(id)); @@ -755,7 +755,9 @@ bool ValidationState_t::IsFloatVectorType(uint32_t id) const { bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeFloat) { return true; @@ -770,13 +772,14 @@ bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt; + return inst && inst->opcode() == SpvOpTypeInt; } bool ValidationState_t::IsIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsIntScalarType(GetComponentType(id)); @@ -787,7 +790,9 @@ bool ValidationState_t::IsIntVectorType(uint32_t id) const { bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeInt) { return true; @@ -802,13 +807,14 @@ bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt && inst->word(3) == 0; + return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 0; } bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsUnsignedIntScalarType(GetComponentType(id)); @@ -819,13 +825,14 @@ bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt && inst->word(3) == 1; + return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 1; } bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsSignedIntScalarType(GetComponentType(id)); @@ -836,13 +843,14 @@ bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { bool ValidationState_t::IsBoolScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeBool; + return inst && inst->opcode() == SpvOpTypeBool; } bool ValidationState_t::IsBoolVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsBoolScalarType(GetComponentType(id)); @@ -853,7 +861,9 @@ bool ValidationState_t::IsBoolVectorType(uint32_t id) const { bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeBool) { return true; @@ -868,7 +878,9 @@ bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsFloatMatrixType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeMatrix) { return IsFloatScalarType(GetComponentType(id)); @@ -923,8 +935,7 @@ bool ValidationState_t::GetStructMemberTypes( bool ValidationState_t::IsPointerType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypePointer; + return inst && inst->opcode() == SpvOpTypePointer; } bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, @@ -942,8 +953,7 @@ bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeCooperativeMatrixNV; + return inst && inst->opcode() == SpvOpTypeCooperativeMatrixNV; } bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const { diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index b014ad62..2b6df04d 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -1580,6 +1580,19 @@ TEST_F(ValidateExtInst, GlslStd450LdexpExpWrongSize) { "number as Result Type")); } +TEST_F(ValidateExtInst, GlslStd450LdexpExpNoType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Ldexp %f32_1 %main_entry +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Ldexp: " + "expected operand Exp to be a 32-bit int scalar " + "or vector type")); +} + TEST_F(ValidateExtInst, GlslStd450FrexpStructSuccess) { const std::string body = R"( %val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f32_h -- cgit v1.2.3 From 8865b202959f9e7a557f525aa69a5c4ddc10c097 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 13 Sep 2021 09:57:44 -0400 Subject: Handle out-of-bounds accesses in VDCE (#4518) It is possible that other optimization will propagate a value into an OpCompositeExtract or OpVectorShuffle instruction that is larger than the vector size. Vector DCE has to be able to handle it. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4513. --- source/opt/vector_dce.cpp | 32 +++++++++++++++------ source/opt/vector_dce.h | 7 +++-- test/opt/vector_dce_test.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 11 deletions(-) diff --git a/source/opt/vector_dce.cpp b/source/opt/vector_dce.cpp index 3c9f9446..28d94a07 100644 --- a/source/opt/vector_dce.cpp +++ b/source/opt/vector_dce.cpp @@ -110,7 +110,11 @@ void VectorDCE::MarkExtractUseAsLive(const Instruction* current_inst, if (current_inst->NumInOperands() < 2) { new_item.components = live_elements; } else { - new_item.components.Set(current_inst->GetSingleWordInOperand(1)); + uint32_t element_index = current_inst->GetSingleWordInOperand(1); + uint32_t item_size = GetVectorComponentCount(operand_inst->type_id()); + if (element_index < item_size) { + new_item.components.Set(element_index); + } } AddItemToWorkListIfNeeded(new_item, live_components, work_list); } @@ -176,10 +180,10 @@ void VectorDCE::MarkVectorShuffleUsesAsLive( second_operand.instruction = def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(1)); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::Vector* first_type = - type_mgr->GetType(first_operand.instruction->type_id())->AsVector(); - uint32_t size_of_first_operand = first_type->element_count(); + uint32_t size_of_first_operand = + GetVectorComponentCount(first_operand.instruction->type_id()); + uint32_t size_of_second_operand = + GetVectorComponentCount(second_operand.instruction->type_id()); for (uint32_t in_op = 2; in_op < current_item.instruction->NumInOperands(); ++in_op) { @@ -187,7 +191,7 @@ void VectorDCE::MarkVectorShuffleUsesAsLive( if (current_item.components.Get(in_op - 2)) { if (index < size_of_first_operand) { first_operand.components.Set(index); - } else { + } else if (index - size_of_first_operand < size_of_second_operand) { second_operand.components.Set(index - size_of_first_operand); } } @@ -202,7 +206,6 @@ void VectorDCE::MarkCompositeContructUsesAsLive( VectorDCE::LiveComponentMap* live_components, std::vector* work_list) { analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); uint32_t current_component = 0; Instruction* current_inst = work_item.instruction; @@ -223,8 +226,7 @@ void VectorDCE::MarkCompositeContructUsesAsLive( assert(HasVectorResult(op_inst)); WorkListItem new_work_item; new_work_item.instruction = op_inst; - uint32_t op_vector_size = - type_mgr->GetType(op_inst->type_id())->AsVector()->element_count(); + uint32_t op_vector_size = GetVectorComponentCount(op_inst->type_id()); for (uint32_t op_vector_idx = 0; op_vector_idx < op_vector_size; op_vector_idx++, current_component++) { @@ -297,6 +299,18 @@ bool VectorDCE::HasScalarResult(const Instruction* inst) const { } } +uint32_t VectorDCE::GetVectorComponentCount(uint32_t type_id) { + assert(type_id != 0 && + "Trying to get the vector element count, but the type id is 0"); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Vector* vector_type = type->AsVector(); + assert( + vector_type && + "Trying to get the vector element count, but the type is not a vector"); + return vector_type->element_count(); +} + bool VectorDCE::RewriteInstructions( Function* function, const VectorDCE::LiveComponentMap& live_components) { bool modified = false; diff --git a/source/opt/vector_dce.h b/source/opt/vector_dce.h index 0df9aee1..4d30b926 100644 --- a/source/opt/vector_dce.h +++ b/source/opt/vector_dce.h @@ -94,12 +94,15 @@ class VectorDCE : public MemPass { // Returns true if the result of |inst| is a vector or a scalar. bool HasVectorOrScalarResult(const Instruction* inst) const; - // Returns true if the result of |inst| is a scalar. + // Returns true if the result of |inst| is a vector. bool HasVectorResult(const Instruction* inst) const; - // Returns true if the result of |inst| is a vector. + // Returns true if the result of |inst| is a scalar. bool HasScalarResult(const Instruction* inst) const; + // Returns the number of elements in the vector type with id |type_id|. + uint32_t GetVectorComponentCount(uint32_t type_id); + // Adds |work_item| to |work_list| if it is not already live according to // |live_components|. |live_components| is updated to indicate that // |work_item| is now live. diff --git a/test/opt/vector_dce_test.cpp b/test/opt/vector_dce_test.cpp index 9bdad375..b14e2256 100644 --- a/test/opt/vector_dce_test.cpp +++ b/test/opt/vector_dce_test.cpp @@ -1351,6 +1351,72 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(VectorDCETest, OutOfBoundsExtract) { + // It tests that the vector DCE pass is able to handle an extract with an + // index that is out of bounds. + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %v4float +; CHECK: OpCompositeExtract %float [[undef]] 8 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_float = OpTypePointer Output %float + %OutColor = OpVariable %_ptr_Output_float Output + %null = OpConstantNull %v4float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %28 = OpLabel + %33 = OpCompositeInsert %v4float %float_1 %null 1 + %extract = OpCompositeExtract %float %33 8 + OpStore %OutColor %extract + OpReturn + OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + +TEST_F(VectorDCETest, OutOfBoundsShuffle) { + // It tests that the vector DCE pass is able to handle a shuffle with an + // index that is out of bounds. + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %v4float +; CHECK: OpVectorShuffle %v4float [[undef]] [[undef]] 9 10 11 12 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %null = OpConstantNull %v4float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %28 = OpLabel + %33 = OpCompositeInsert %v4float %float_1 %null 1 + %shuffle = OpVectorShuffle %v4float %33 %33 9 10 11 12 + OpStore %OutColor %shuffle + OpReturn + OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From 5efeaad30984ad62626c5f623776b49e27e1fe4c Mon Sep 17 00:00:00 2001 From: alan-baker Date: Mon, 13 Sep 2021 11:16:37 -0400 Subject: Fix bad order of checks in atomic validation (#4524) Fixes https://crbug.com/38625 * Check the type is an integer before checking it's bit width --- source/val/validate_atomics.cpp | 2 +- test/val/val_atomics_test.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index da023b80..cfa15d9f 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp @@ -184,7 +184,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { } // Can't use result_type because OpAtomicStore doesn't have a result - if (_.GetBitWidth(data_type) == 64 && _.IsIntScalarType(data_type) && + if ( _.IsIntScalarType(data_type) &&_.GetBitWidth(data_type) == 64 && !_.HasCapability(SpvCapabilityInt64Atomics)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index b7f6948e..d1a030a8 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp @@ -2679,6 +2679,39 @@ OpFunctionEnd "CooperativeMatrixNV capability is present")); } +TEST_F(ValidateAtomics, IIncrementBadPointerDataType) { + const std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + %uint = OpTypeInt 32 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %16 = OpTypeFunction %void +%uint_538976288 = OpConstant %uint 538976288 + %int = OpTypeInt 32 1 +%_runtimearr_int = OpTypeRuntimeArray %int + %_struct_5 = OpTypeStruct %_runtimearr_int +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %3 = OpVariable %_ptr_Input_v3uint Input + %7 = OpVariable %_ptr_Uniform__struct_5 Uniform + %8224 = OpFunction %void None %16 + %65312 = OpLabel + %25 = OpAccessChain %_ptr_Input_uint %3 %uint_538976288 + %26 = OpLoad %uint %25 + %2097184 = OpAtomicIIncrement %int %7 %uint_538976288 %26 + OpUnreachable + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicIIncrement: expected Pointer to point to a " + "value of type Result Type")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From bf463fe37c5a425397367068dc99cf5da4d27527 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 13 Sep 2021 16:55:51 +0100 Subject: Fix UBSan kokoro config (#4522) A test has been removed which depends on casting to spv_target_env from a value outside the range of that enum. This is an undefined behaviour, thus the test is invalid. --- kokoro/scripts/linux/build-docker.sh | 2 +- test/text_to_binary_test.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh index 84457a1b..8f76803c 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh @@ -74,7 +74,7 @@ if [ $TOOL = "cmake" ]; then # encountered - additional compiler options are required to force this. # The -DSPIRV_USE_SANITIZER=undefined option instructs SPIR-V Tools to be # built with UBSan enabled. - ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=undefined -DENABLE_RTTI=ON -DCMAKE_C_FLAGS=-fno-sanitize-recover -DCMAKE_CXX_FLAGS=-fno-sanitize-recover" + ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=undefined -DENABLE_RTTI=ON -DCMAKE_C_FLAGS=-fno-sanitize-recover=all -DCMAKE_CXX_FLAGS=-fno-sanitize-recover=all" [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } elif [ $CONFIG = "COVERAGE" ]; then ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON" diff --git a/test/text_to_binary_test.cpp b/test/text_to_binary_test.cpp index 57f0a6ce..99d9ed68 100644 --- a/test/text_to_binary_test.cpp +++ b/test/text_to_binary_test.cpp @@ -247,12 +247,6 @@ INSTANTIATE_TEST_SUITE_P( {"0x1.804p4", 0x00004e01}, })); -TEST(CreateContext, InvalidEnvironment) { - spv_target_env env; - std::memset(&env, 99, sizeof(env)); - EXPECT_THAT(spvContextCreate(env), IsNull()); -} - TEST(CreateContext, UniversalEnvironment) { auto c = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); EXPECT_THAT(c, NotNull()); -- cgit v1.2.3 From 846b032b5352bf0f84692953cf266859be5a3492 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Mon, 13 Sep 2021 13:19:04 -0400 Subject: Fix infinite loop in validation (#4523) * Fix infinite loop in validation Fixes https://crbug.com/38548 * Fixes an issue in structured exit checking where an invalid merge could result in an infinite traversal * formatting --- source/val/construct.cpp | 3 ++- test/val/val_cfg_test.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/source/val/construct.cpp b/source/val/construct.cpp index 53008690..251e2bba 100644 --- a/source/val/construct.cpp +++ b/source/val/construct.cpp @@ -181,8 +181,9 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { for (auto& use : block->label()->uses()) { if ((use.first->opcode() == SpvOpLoopMerge || use.first->opcode() == SpvOpSelectionMerge) && - use.second == 1) + use.second == 1 && use.first->block()->dominates(*block)) { return use.first->block(); + } } return block->immediate_dominator(); }; diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index dd57f018..c2120b56 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -4482,6 +4482,42 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); } +TEST_F(ValidateCFG, BadStructuredExitBackwardsMerge) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %4 %5 None +OpBranchConditional %undef %4 %6 +%6 = OpLabel +OpSelectionMerge %7 None +OpBranchConditional %undef %8 %9 +%7 = OpLabel +OpReturn +%8 = OpLabel +OpBranch %5 +%9 = OpLabel +OpSelectionMerge %6 None +OpBranchConditional %undef %5 %5 +%5 = OpLabel +OpBranch %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From cb6c66917ac8a1f92c5499fda1efbbf76ada7702 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 14 Sep 2021 12:20:20 -0400 Subject: Roll external/googletest/ b7d472f12..955c7f837 (111 commits) (#4521) https://github.com/google/googletest/compare/b7d472f1225c...955c7f837efa $ git log b7d472f12..955c7f837 --date=short --no-merges --format='%ad %ae %s' 2021-08-20 absl-team Googletest export 2021-08-20 absl-team Googletest export 2021-08-19 invalid_ms_user Simplify example code with c++11 2021-08-12 absl-team Googletest export 2021-08-10 dmauro Googletest export 2021-08-09 dmauro Googletest export 2021-08-06 absl-team Internal change 2021-08-06 victordk13 Format test properties in xml printer 2021-08-05 iamberkeyavas typo fix gmock_cook_book 2021-08-03 absl-team Googletest export 2021-08-03 absl-team Googletest export 2021-08-02 absl-team Googletest export 2021-07-29 absl-team Googletest export 2021-07-29 otnaiud Typo, double "the" 2021-07-28 absl-team Internal change 2021-07-24 slowy.arfy fix: typo codespelling comment 2021-07-22 absl-team Googletest export 2021-07-21 absl-team Googletest export 2021-07-19 dmauro Googletest export 2021-07-14 761129+derekmauro Fix location of GOOGLETEST_CM0011 marker 2021-07-08 absl-team Googletest export 2021-07-08 absl-team Googletest export 2021-07-07 absl-team Googletest export 2021-07-02 absl-team Googletest export 2021-07-02 absl-team Googletest export 2021-07-07 manuel Link -lregex on QNX 2021-07-01 jeremy.nimmer Use GTEST_DONT_DEFINE_TEST_F to guard TEST_F 2021-06-29 absl-team Googletest export 2021-06-28 absl-team Googletest export 2021-06-28 absl-team Googletest export 2021-06-25 manuel Don't link pthread on QNX 2021-06-23 alex Fix EXPECT_DEATH() and ASSERT_DEATH() triggering -Wcovered-switch-default 2021-06-18 dmauro Googletest export 2021-06-03 pochkaenkov feat: make a matcher ElementsAreArray applicable for std ranges 2021-06-13 hyuk.myeong fix typos 2021-06-11 absl-team Googletest export 2021-06-10 absl-team Googletest export 2021-06-09 absl-team Googletest export 2021-06-09 dmauro Googletest export 2021-06-09 absl-team Googletest export 2021-06-09 absl-team Googletest export 2021-06-08 absl-team Googletest export 2021-06-04 absl-team Googletest export 2021-06-03 dmauro Googletest export 2021-05-28 florin.crisan #3420 Declare MarkAsIgnored as a DLL export 2021-05-28 florin.crisan #3420 Properly declare all overloads of testing::internal::PrintTo as DLL exports 2021-06-01 absl-team Googletest export 2021-06-01 absl-team Googletest export 2021-05-26 absl-team Googletest export 2021-05-25 absl-team Googletest export (...) 2021-05-06 durandal Googletest export 2021-05-06 absl-team Googletest export 2021-05-03 absl-team Googletest export 2021-05-04 JC3 isalnum -> IsAlNum for correct handling of signed chars 2021-04-28 absl-team Googletest export 2021-04-27 absl-team Googletest export 2021-04-27 absl-team Googletest export 2021-04-27 absl-team Googletest export 2021-04-26 absl-team Googletest export 2021-04-26 absl-team Googletest export 2021-04-20 absl-team Googletest export 2021-04-19 absl-team Googletest export 2021-04-20 github Use URL instead of git-repo 2021-04-16 dmauro Googletest export 2021-04-14 absl-team Googletest export 2021-04-15 sebkraemer Apply missing suggestions from code review for GTEST_SKIP 2021-04-15 sebkraemer Apply suggestions from code review for GTEST_SKIP documentation 2020-12-15 sebkraemer Add subsection for GTEST_SKIP documentation 2021-04-15 jbampton chore: fix spelling 2021-04-14 github Mention to explicitely set the option to it's default. 2021-04-14 dmauro Googletest export 2021-04-14 github Changes like Requested. 2021-04-13 absl-team Googletest export 2021-04-09 absl-team Googletest export 2021-04-09 absl-team Googletest export 2021-04-09 absl-team Googletest export 2021-04-07 absl-team Googletest export 2021-04-07 absl-team Googletest export 2021-04-07 absl-team Googletest export 2021-04-06 absl-team Googletest export 2021-04-06 absl-team Googletest export 2021-04-05 absl-team Googletest export 2021-04-02 absl-team Googletest export 2021-03-25 absl-team Googletest export 2021-03-24 absl-team Googletest export 2021-03-24 absl-team Googletest export 2021-03-24 absl-team Googletest export 2021-03-24 absl-team Googletest export 2021-03-23 absl-team Googletest export 2021-03-22 absl-team Googletest export 2021-03-22 absl-team Googletest export 2021-03-22 absl-team Googletest export 2021-03-20 absl-team Googletest export 2021-03-17 77407429+a-sully Update nicestrictnaggy gmock cook_book links 2021-03-14 zekewarren Use @platforms instead of @bazel_tools for windows constraint 2021-03-13 github Use Fetchcontent instead of ExternalProject 2020-12-30 mattias.ellert Port to GNU/Hurd 2020-12-24 georgthegreat Use proper feature test macro to test if library supports char8_t 2020-12-05 gautham.bangalore Fix typo 2020-12-04 zed.three CMake: Add namespaced ALIAS library Created with: roll-dep external/googletest Roll external/re2/ f8e389f3a..4244cd1cb (10 commits) https://github.com/google/re2/compare/f8e389f3acdc...4244cd1cb492 $ git log f8e389f3a..4244cd1cb --date=short --no-merges --format='%ad %ae %s' 2021-05-21 junyer Fix a bug in `Regexp::ToString()`. 2021-05-19 junyer Pass a path to `cmake` in order to avoid a warning. 2021-05-18 junyer Let CMake pass `-pthread` for us. 2021-05-18 junyer Remove unneeded policy setting. 2021-05-12 junyer (|a)* shouldn't match more text than (|a)+ does! 2021-05-10 junyer Update the R wrapper URL. 2021-05-03 junyer Add GCC 11 to the build matrix. 2021-04-29 junyer Nullify hooks::context when using RE2::Set. 2021-04-19 junyer Add Clang 12 to the build matrix. 2021-03-26 junyer Set "compatibility version" and "current version". Created with: roll-dep external/re2 Roll external/spirv-headers/ e71feddb3..0d3c45cdb (6 commits) https://github.com/KhronosGroup/SPIRV-Headers/compare/e71feddb3f17...0d3c45cdbb45 $ git log e71feddb3..0d3c45cdb --date=short --no-merges --format='%ad %ae %s' 2021-08-27 greg Add nonsemantic.shader.debuginfo to Bazel 2021-07-28 marijn spirv.core.grammar: Remove duplicate OpArbitraryFloatPowNINTEL declaration 2021-05-20 kloczek Rename spirv-headers.pc to SPIRV-Headers.pc 2021-05-19 kloczek removed excesive space in configure_file() line 2021-05-19 kloczek Fixed substituted string with paths and version 2021-05-18 kloczek Add spirv-headers pkgconfig file Created with: roll-dep external/spirv-headers --- DEPS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 92c67e05..ef37c6c2 100644 --- a/DEPS +++ b/DEPS @@ -4,9 +4,9 @@ vars = { 'github': 'https://github.com', 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', - 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382', - 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683', - 'spirv_headers_revision': 'e71feddb3f17c5586ff7f4cfb5ed1258b800574b', + 'googletest_revision': '955c7f837efad184ec63e771c42542d37545eaef', + 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', + 'spirv_headers_revision': '0d3c45cdbb4563b95be9037ea967aac815caf78f', } deps = { -- cgit v1.2.3 From 36ff135341989364e8ea8fcc2b6d8fec9dac0f8f Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 14 Sep 2021 22:09:05 +0100 Subject: spirv-opt: Avoid integer overflow during constant folding (#4511) In SPIR-V, integers use 2s complement representation, so that signed integer overflow and underflow is well defined. However, the constant folder was causing overflow / underflow at the C++ level. This change avoids such overflows by performing constant folding for IAdd, ISub and IMul in the context of unsigned values, which works because signedness is irrelevant according to the SPIR-V semantics for these instructions. Fixes #4510. --- source/opt/folding_rules.cpp | 34 +++--- test/opt/fold_test.cpp | 259 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 273 insertions(+), 20 deletions(-) diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 6ae078fb..20051a6b 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -523,7 +523,8 @@ uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr, float fval = val.getAsFloat(); \ if (!IsValidResult(fval)) return 0; \ words = val.GetWords(); \ - } static_assert(true, "require extra semicolon") + } \ + static_assert(true, "require extra semicolon") switch (opcode) { case SpvOpFMul: FOLD_OP(*); @@ -558,24 +559,19 @@ uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr, uint32_t width = type->AsInteger()->width(); assert(width == 32 || width == 64); std::vector words; -#define FOLD_OP(op) \ - if (width == 64) { \ - if (type->IsSigned()) { \ - int64_t val = input1->GetS64() op input2->GetS64(); \ - words = ExtractInts(static_cast(val)); \ - } else { \ - uint64_t val = input1->GetU64() op input2->GetU64(); \ - words = ExtractInts(val); \ - } \ - } else { \ - if (type->IsSigned()) { \ - int32_t val = input1->GetS32() op input2->GetS32(); \ - words.push_back(static_cast(val)); \ - } else { \ - uint32_t val = input1->GetU32() op input2->GetU32(); \ - words.push_back(val); \ - } \ - } static_assert(true, "require extra semicalon") + // Regardless of the sign of the constant, folding is performed on an unsigned + // interpretation of the constant data. This avoids signed integer overflow + // while folding, and works because sign is irrelevant for the IAdd, ISub and + // IMul instructions. +#define FOLD_OP(op) \ + if (width == 64) { \ + uint64_t val = input1->GetU64() op input2->GetU64(); \ + words = ExtractInts(val); \ + } else { \ + uint32_t val = input1->GetU32() op input2->GetU32(); \ + words.push_back(val); \ + } \ + static_assert(true, "require extra semicolon") switch (opcode) { case SpvOpIMul: FOLD_OP(*); diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index da5b017d..292a869e 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -137,6 +137,7 @@ OpName %main "main" %int = OpTypeInt 32 1 %long = OpTypeInt 64 1 %uint = OpTypeInt 32 0 +%ulong = OpTypeInt 64 0 %v2int = OpTypeVector %int 2 %v4int = OpTypeVector %int 4 %v4float = OpTypeVector %float 4 @@ -154,6 +155,7 @@ OpName %main "main" %_ptr_double = OpTypePointer Function %double %_ptr_half = OpTypePointer Function %half %_ptr_long = OpTypePointer Function %long +%_ptr_ulong = OpTypePointer Function %ulong %_ptr_v2int = OpTypePointer Function %v2int %_ptr_v4int = OpTypePointer Function %v4int %_ptr_v4float = OpTypePointer Function %v4float @@ -171,12 +173,23 @@ OpName %main "main" %int_2 = OpConstant %int 2 %int_3 = OpConstant %int 3 %int_4 = OpConstant %int 4 +%int_10 = OpConstant %int 10 +%int_1073741824 = OpConstant %int 1073741824 +%int_n1 = OpConstant %int -1 %int_n24 = OpConstant %int -24 +%int_n858993459 = OpConstant %int -858993459 %int_min = OpConstant %int -2147483648 %int_max = OpConstant %int 2147483647 %long_0 = OpConstant %long 0 +%long_1 = OpConstant %long 1 %long_2 = OpConstant %long 2 %long_3 = OpConstant %long 3 +%long_10 = OpConstant %long 10 +%long_4611686018427387904 = OpConstant %long 4611686018427387904 +%long_n1 = OpConstant %long -1 +%long_n3689348814741910323 = OpConstant %long -3689348814741910323 +%long_min = OpConstant %long -9223372036854775808 +%long_max = OpConstant %long 9223372036854775807 %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 %uint_2 = OpConstant %uint 2 @@ -184,7 +197,13 @@ OpName %main "main" %uint_4 = OpConstant %uint 4 %uint_32 = OpConstant %uint 32 %uint_42 = OpConstant %uint 42 +%uint_2147483649 = OpConstant %uint 2147483649 %uint_max = OpConstant %uint 4294967295 +%ulong_0 = OpConstant %ulong 0 +%ulong_1 = OpConstant %ulong 1 +%ulong_2 = OpConstant %ulong 2 +%ulong_9223372036854775809 = OpConstant %ulong 9223372036854775809 +%ulong_max = OpConstant %ulong 18446744073709551615 %v2int_undef = OpUndef %v2int %v2int_0_0 = OpConstantComposite %v2int %int_0 %int_0 %v2int_1_0 = OpConstantComposite %v2int %int_1 %int_0 @@ -5572,7 +5591,109 @@ INSTANTIATE_TEST_SUITE_P(MergeMulTest, MatchingInstructionFoldingTest, "%5 = OpFMul %float %4 %2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 5, true) + 5, true), + // Test case 25: fold overflowing signed 32 bit imuls + // (x * 1073741824) * 2 = x * int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_1073741824\n" + + "%4 = OpIMul %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 26: fold overflowing signed 64 bit imuls + // (x * 4611686018427387904) * 2 = x * long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIMul %long %2 %long_4611686018427387904\n" + + "%4 = OpIMul %long %3 %long_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 27: fold overflowing 32 bit unsigned imuls + // (x * 2147483649) * 2 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: %4 = OpIMul [[uint]] [[ld]] [[uint_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpIMul %uint %2 %uint_2147483649\n" + + "%4 = OpIMul %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 28: fold overflowing 64 bit unsigned imuls + // (x * 9223372036854775809) * 2 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" + + "; CHECK: [[ulong_2:%\\w+]] = OpConstant [[ulong]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" + + "; CHECK: %4 = OpIMul [[ulong]] [[ld]] [[ulong_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_ulong Function\n" + + "%2 = OpLoad %ulong %var\n" + + "%3 = OpIMul %ulong %2 %ulong_9223372036854775809\n" + + "%4 = OpIMul %ulong %3 %ulong_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 29: fold underflowing signed 32 bit imuls + // (x * (-858993459)) * 10 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_n858993459\n" + + "%4 = OpIMul %int %3 %int_10\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 30: fold underflowing signed 64 bit imuls + // (x * (-3689348814741910323)) * 10 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIMul %long %2 %long_n3689348814741910323\n" + + "%4 = OpIMul %long %3 %long_10\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) )); INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, @@ -6052,6 +6173,108 @@ INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, "%4 = OpFAdd %float %float_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 12: fold overflowing signed 32 bit iadds + // (x + int_max) + 1 = x + int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %2 %int_max\n" + + "%4 = OpIAdd %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: fold overflowing signed 64 bit iadds + // (x + long_max) + 1 = x + long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIAdd %long %2 %long_max\n" + + "%4 = OpIAdd %long %3 %long_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 14: fold overflowing 32 bit unsigned iadds + // (x + uint_max) + 2 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: %4 = OpIAdd [[uint]] [[ld]] [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpIAdd %uint %2 %uint_max\n" + + "%4 = OpIAdd %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: fold overflowing 64 bit unsigned iadds + // (x + ulong_max) + 2 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" + + "; CHECK: [[ulong_1:%\\w+]] = OpConstant [[ulong]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" + + "; CHECK: %4 = OpIAdd [[ulong]] [[ld]] [[ulong_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_ulong Function\n" + + "%2 = OpLoad %ulong %var\n" + + "%3 = OpIAdd %ulong %2 %ulong_max\n" + + "%4 = OpIAdd %ulong %3 %ulong_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 16: fold underflowing signed 32 bit iadds + // (x + int_min) + (-1) = x + int_max + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_max:%\\w+]] = OpConstant [[int]] 2147483647\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_max]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %2 %int_min\n" + + "%4 = OpIAdd %int %3 %int_n1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 17: fold underflowing signed 64 bit iadds + // (x + long_min) + (-1) = x + long_max + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_max:%\\w+]] = OpConstant [[long]] 9223372036854775807\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_max]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIAdd %long %2 %long_min\n" + + "%4 = OpIAdd %long %3 %long_n1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) )); @@ -6420,6 +6643,40 @@ INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest, "%4 = OpISub %int %int_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 14: fold overflowing signed 32 bit isubs + // (x - int_max) - 1 = x - int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpISub [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpISub %int %2 %int_max\n" + + "%4 = OpISub %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: fold overflowing signed 64 bit isubs + // (x - long_max) - 1 = x - long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpISub %long %2 %long_max\n" + + "%4 = OpISub %long %3 %long_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) )); -- cgit v1.2.3 From 9e65f054d1a13f64c2f1e4fa7b55791e35fbea6e Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 14 Sep 2021 22:09:39 +0100 Subject: spirv-fuzz: Account for differing signedness in WrapVectorSynonym (#4414) Makes the fuzzer pass and transformation that wraps vector synonyms aware of the fact that integer operations can have arguments that differ in signedness, and that the result type of such an operation can have different sign from the argument types. Fixes #4413. --- source/fuzz/fuzzer_pass_apply_id_synonyms.cpp | 2 +- source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp | 63 ++--- source/fuzz/fuzzer_util.cpp | 87 +++++++ source/fuzz/fuzzer_util.h | 15 ++ .../transformation_replace_id_with_synonym.cpp | 94 +------- .../fuzz/transformation_replace_id_with_synonym.h | 15 -- source/fuzz/transformation_wrap_vector_synonym.cpp | 29 ++- source/fuzz/transformation_wrap_vector_synonym.h | 7 +- test/fuzz/fuzzerutil_test.cpp | 253 ++++++++++++++++++++ ...transformation_replace_id_with_synonym_test.cpp | 256 --------------------- .../transformation_wrap_vector_synonym_test.cpp | 159 +++++++++++++ 11 files changed, 581 insertions(+), 399 deletions(-) diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 8965c15e..5c3b86b9 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -198,7 +198,7 @@ bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes( GetIRContext(), base_object_type_id_2, dd2.index()); assert(type_id_1 && type_id_2 && "Data descriptors have invalid types"); - return TransformationReplaceIdWithSynonym::TypesAreCompatible( + return fuzzerutil::TypesAreCompatible( GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2); } diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp index 37c93b30..35adcfec 100644 --- a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp +++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -55,21 +55,12 @@ void FuzzerPassWrapVectorSynonym::Apply() { SpvOpCompositeConstruct, instruction_iterator)) { return; } - // Get the scalar type represented by the targeted instruction id. - uint32_t operand_type_id = instruction_iterator->type_id(); - // Get a random vector size from 2 to 4. - uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector(); - - // Randomly choose a position that target ids should be placed at. - // The position is in range [0, n - 1], where n is the size of the - // vector. - uint32_t position = - GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size); - - // Target ids are the two scalar ids from the original instruction. - uint32_t target_id1 = instruction_iterator->GetSingleWordInOperand(0); - uint32_t target_id2 = instruction_iterator->GetSingleWordInOperand(1); + // Get the scalar operands from the original instruction. + opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(0)); + opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(1)); // We need to be able to make a synonym of the scalar operation's result // id, as well as the operand ids (for example, they cannot be @@ -80,16 +71,23 @@ void FuzzerPassWrapVectorSynonym::Apply() { return; } if (!fuzzerutil::CanMakeSynonymOf( - GetIRContext(), *GetTransformationContext(), - *GetIRContext()->get_def_use_mgr()->GetDef(target_id1))) { + GetIRContext(), *GetTransformationContext(), *operand1)) { return; } if (!fuzzerutil::CanMakeSynonymOf( - GetIRContext(), *GetTransformationContext(), - *GetIRContext()->get_def_use_mgr()->GetDef(target_id2))) { + GetIRContext(), *GetTransformationContext(), *operand2)) { return; } + // Get a random vector size from 2 to 4. + uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector(); + + // Randomly choose a position that target ids should be placed at. + // The position is in range [0, n - 1], where n is the size of the + // vector. + uint32_t position = + GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size); + // Stores the ids of scalar constants. std::vector vec1_components; std::vector vec2_components; @@ -97,33 +95,42 @@ void FuzzerPassWrapVectorSynonym::Apply() { // Populate components based on vector type and size. for (uint32_t i = 0; i < vector_size; ++i) { if (i == position) { - vec1_components.emplace_back(target_id1); - vec2_components.emplace_back(target_id2); + vec1_components.emplace_back(operand1->result_id()); + vec2_components.emplace_back(operand2->result_id()); } else { vec1_components.emplace_back( - FindOrCreateZeroConstant(operand_type_id, true)); + FindOrCreateZeroConstant(operand1->type_id(), true)); vec2_components.emplace_back( - FindOrCreateZeroConstant(operand_type_id, true)); + FindOrCreateZeroConstant(operand2->type_id(), true)); } } // Add two OpCompositeConstruct to the module with result id returned. - const uint32_t vector_type_id = - FindOrCreateVectorType(operand_type_id, vector_size); + // The added vectors may have different types, for instance if the + // scalar instruction operates on integers with differing sign. // Add the first OpCompositeConstruct that wraps the id of the first // operand. uint32_t result_id1 = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationCompositeConstruct( - vector_type_id, vec1_components, instruction_descriptor, - result_id1)); + FindOrCreateVectorType(operand1->type_id(), vector_size), + vec1_components, instruction_descriptor, result_id1)); // Add the second OpCompositeConstruct that wraps the id of the second // operand. uint32_t result_id2 = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationCompositeConstruct( - vector_type_id, vec2_components, instruction_descriptor, - result_id2)); + FindOrCreateVectorType(operand2->type_id(), vector_size), + vec2_components, instruction_descriptor, result_id2)); + + // The result of the vector instruction that + // TransformationWrapVectorSynonym will create should be a vector of the + // right size, with the scalar instruction's result type as its element + // type. This can be distinct from the types of the operands, if the + // scalar instruction adds two signed integers and stores the result in + // an unsigned id, for example. A transformation is applied to add the + // right type to the module. + FindOrCreateVectorType(instruction_iterator->type_id(), vector_size); // Apply transformation to do vector operation and add synonym between // the result vector id and the id of the original instruction. diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index ea7cde7f..1d368a9f 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -2018,6 +2018,93 @@ opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, }); } +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all +// opcodes that are agnostic to signedness of operands to function. +// This is not exhaustive yet. +bool IsAgnosticToSignednessOfOperand(SpvOp opcode, + uint32_t use_in_operand_index) { + switch (opcode) { + case SpvOpSNegate: + case SpvOpNot: + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + case SpvOpSDiv: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + return true; + + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT, + // AtomicFloat64AddEXT. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index == 1 || use_in_operand_index == 2; + + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1 && use_in_operand_index <= 3; + + case SpvOpAtomicLoad: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicFlagTestAndSet: // Capability Kernel. + case SpvOpAtomicFlagClear: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1; + + case SpvOpAccessChain: + // The signedness of indices does not matter. + return use_in_operand_index > 0; + + default: + // Conservatively assume that the id cannot be swapped in other + // instructions. + return false; + } +} + +bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2) { + assert(ir_context->get_type_mgr()->GetType(type_id_1) && + ir_context->get_type_mgr()->GetType(type_id_2) && + "Type ids are invalid"); + + return type_id_1 == type_id_2 || + (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && + fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); +} + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index e4697a1a..54aa14a2 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -604,6 +604,21 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, uint32_t function_id); +// Returns true if the instruction with opcode |opcode| does not change its +// behaviour depending on the signedness of the operand at +// |use_in_operand_index|. +// Assumes that the operand must be the id of an integer scalar or vector. +bool IsAgnosticToSignednessOfOperand(SpvOp opcode, + uint32_t use_in_operand_index); + +// Returns true if |type_id_1| and |type_id_2| represent compatible types +// given the context of the instruction with |opcode| (i.e. we can replace +// an operand of |opcode| of the first type with an id of the second type +// and vice-versa). +bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2); + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp index 14555315..8d21d233 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -65,9 +65,10 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( // If the id of interest and the synonym are scalar or vector integer // constants with different signedness, their use can only be swapped if the // instruction is agnostic to the signedness of the operand. - if (!TypesAreCompatible(ir_context, use_instruction->opcode(), - message_.id_use_descriptor().in_operand_index(), - type_id_of_interest, type_id_synonym)) { + if (!fuzzerutil::TypesAreCompatible( + ir_context, use_instruction->opcode(), + message_.id_use_descriptor().in_operand_index(), type_id_of_interest, + type_id_synonym)) { return false; } @@ -109,93 +110,6 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() return result; } -// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all -// opcodes that are agnostic to signedness of operands to function. -// This is not exhaustive yet. -bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand( - SpvOp opcode, uint32_t use_in_operand_index) { - switch (opcode) { - case SpvOpSNegate: - case SpvOpNot: - case SpvOpIAdd: - case SpvOpISub: - case SpvOpIMul: - case SpvOpSDiv: - case SpvOpSRem: - case SpvOpSMod: - case SpvOpShiftRightLogical: - case SpvOpShiftRightArithmetic: - case SpvOpShiftLeftLogical: - case SpvOpBitwiseOr: - case SpvOpBitwiseXor: - case SpvOpBitwiseAnd: - case SpvOpIEqual: - case SpvOpINotEqual: - case SpvOpULessThan: - case SpvOpSLessThan: - case SpvOpUGreaterThan: - case SpvOpSGreaterThan: - case SpvOpULessThanEqual: - case SpvOpSLessThanEqual: - case SpvOpUGreaterThanEqual: - case SpvOpSGreaterThanEqual: - return true; - - case SpvOpAtomicStore: - case SpvOpAtomicExchange: - case SpvOpAtomicIAdd: - case SpvOpAtomicISub: - case SpvOpAtomicSMin: - case SpvOpAtomicUMin: - case SpvOpAtomicSMax: - case SpvOpAtomicUMax: - case SpvOpAtomicAnd: - case SpvOpAtomicOr: - case SpvOpAtomicXor: - case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT, - // AtomicFloat64AddEXT. - assert(use_in_operand_index != 0 && - "Signedness check should not occur on a pointer operand."); - return use_in_operand_index == 1 || use_in_operand_index == 2; - - case SpvOpAtomicCompareExchange: - case SpvOpAtomicCompareExchangeWeak: // Capability Kernel. - assert(use_in_operand_index != 0 && - "Signedness check should not occur on a pointer operand."); - return use_in_operand_index >= 1 && use_in_operand_index <= 3; - - case SpvOpAtomicLoad: - case SpvOpAtomicIIncrement: - case SpvOpAtomicIDecrement: - case SpvOpAtomicFlagTestAndSet: // Capability Kernel. - case SpvOpAtomicFlagClear: // Capability Kernel. - assert(use_in_operand_index != 0 && - "Signedness check should not occur on a pointer operand."); - return use_in_operand_index >= 1; - - case SpvOpAccessChain: - // The signedness of indices does not matter. - return use_in_operand_index > 0; - - default: - // Conservatively assume that the id cannot be swapped in other - // instructions. - return false; - } -} - -bool TransformationReplaceIdWithSynonym::TypesAreCompatible( - opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index, - uint32_t type_id_1, uint32_t type_id_2) { - assert(ir_context->get_type_mgr()->GetType(type_id_1) && - ir_context->get_type_mgr()->GetType(type_id_2) && - "Type ids are invalid"); - - return type_id_1 == type_id_2 || - (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && - fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); -} - std::unordered_set TransformationReplaceIdWithSynonym::GetFreshIds() const { return std::unordered_set(); diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h index 1ac636b4..4570fcef 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.h +++ b/source/fuzz/transformation_replace_id_with_synonym.h @@ -52,22 +52,7 @@ class TransformationReplaceIdWithSynonym : public Transformation { protobufs::Transformation ToMessage() const override; - // Returns true if |type_id_1| and |type_id_2| represent compatible types - // given the context of the instruction with |opcode| (i.e. we can replace - // an operand of |opcode| of the first type with an id of the second type - // and vice-versa). - static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, - uint32_t use_in_operand_index, - uint32_t type_id_1, uint32_t type_id_2); - private: - // Returns true if the instruction with opcode |opcode| does not change its - // behaviour depending on the signedness of the operand at - // |use_in_operand_index|. - // Assumes that the operand must be the id of an integer scalar or vector. - static bool IsAgnosticToSignednessOfOperand(SpvOp opcode, - uint32_t use_in_operand_index); - protobufs::TransformationReplaceIdWithSynonym message_; }; diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp index d0eddd44..490bcd78 100644 --- a/source/fuzz/transformation_wrap_vector_synonym.cpp +++ b/source/fuzz/transformation_wrap_vector_synonym.cpp @@ -70,16 +70,29 @@ bool TransformationWrapVectorSynonym::IsApplicable( return false; } - // The 2 vectors must be the same valid vector type. + // The 2 vectors must have compatible vector types. auto vec1_type_id = vec1->type_id(); auto vec2_type_id = vec2->type_id(); - if (vec1_type_id != vec2_type_id) { + for (auto operand_index : {0, 1}) { + if (!fuzzerutil::TypesAreCompatible(ir_context, instruction->opcode(), + operand_index, vec1_type_id, + vec2_type_id)) { + return false; + } + } + + auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id); + if (vec1_type->opcode() != SpvOpTypeVector) { return false; } - if (ir_context->get_def_use_mgr()->GetDef(vec1_type_id)->opcode() != - SpvOpTypeVector) { + // A suitable vector for the result type of the new vector instruction must + // exist in the module. This is a vector of the right length, whose element + // type matches the result type of the scalar instruction. + uint32_t vector_size = vec1_type->GetSingleWordInOperand(1); + if (!fuzzerutil::MaybeGetVectorType(ir_context, instruction->type_id(), + vector_size)) { return false; } @@ -124,9 +137,11 @@ void TransformationWrapVectorSynonym::Apply( // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1 // %result_id2. - auto vec_type_id = ir_context->get_def_use_mgr() - ->GetDef(message_.vector_operand1()) - ->type_id(); + auto vector_operand_type = ir_context->get_def_use_mgr()->GetDef( + fuzzerutil::GetTypeId(ir_context, message_.vector_operand1())); + uint32_t vector_size = vector_operand_type->GetSingleWordInOperand(1); + auto vec_type_id = fuzzerutil::MaybeGetVectorType( + ir_context, instruction->type_id(), vector_size); auto new_instruction = MakeUnique( ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(), std::move(in_operands)); diff --git a/source/fuzz/transformation_wrap_vector_synonym.h b/source/fuzz/transformation_wrap_vector_synonym.h index 008211a2..94437fe5 100644 --- a/source/fuzz/transformation_wrap_vector_synonym.h +++ b/source/fuzz/transformation_wrap_vector_synonym.h @@ -38,11 +38,14 @@ class TransformationWrapVectorSynonym : public Transformation { // two vector operands. // - |fresh_id| is an unused id that will be used as a result id of the // created instruction. - // - |vector_operand1| and |vector_operand2| must have the same vector type - // that is supported by this transformation. + // - |vector_operand1| and |vector_operand2| must have compatible vector types + // that are supported by this transformation. // - |pos| is an index of the operands of |instruction_id| in the // |vector_operand1| and |vector_operand2|. It must be less than the size // of those vector operands. + // - A vector type with the same width as the types of the vector operands, + // and element type matching the type of |instruction_id|, must exist in the + // module. bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp index 6771c404..0ad3e742 100644 --- a/test/fuzz/fuzzerutil_test.cpp +++ b/test/fuzz/fuzzerutil_test.cpp @@ -1549,6 +1549,259 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetZeroConstantTest) { float_ids.end()); } +TEST(FuzzerutilTest, TypesAreCompatible) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeInt 32 0 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %86 = OpTypeStruct %9 + %87 = OpTypePointer Workgroup %86 + %88 = OpVariable %87 Workgroup + %89 = OpTypePointer Workgroup %9 + %19 = OpConstant %9 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 7 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %90 = OpAccessChain %89 %88 %19 + %21 = OpAtomicLoad %6 %14 %15 %20 + %22 = OpAtomicExchange %6 %14 %15 %20 %16 + %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 + %24 = OpAtomicIIncrement %6 %14 %15 %20 + %25 = OpAtomicIDecrement %6 %14 %15 %20 + %26 = OpAtomicIAdd %6 %14 %15 %20 %16 + %27 = OpAtomicISub %6 %14 %15 %20 %16 + %28 = OpAtomicSMin %6 %14 %15 %20 %16 + %29 = OpAtomicUMin %9 %90 %15 %20 %18 + %30 = OpAtomicSMax %6 %14 %15 %20 %15 + %31 = OpAtomicUMax %9 %90 %15 %20 %18 + %32 = OpAtomicAnd %6 %14 %15 %20 %16 + %33 = OpAtomicOr %6 %14 %15 %20 %16 + %34 = OpAtomicXor %6 %14 %15 %20 %16 + OpAtomicStore %14 %15 %20 %16 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const uint32_t int_type = 6; // The id of OpTypeInt 32 1 + const uint32_t uint_type = 9; // The id of OpTypeInt 32 0 + + // OpAtomicLoad +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 2, + int_type, uint_type)); + + // OpAtomicExchange +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange, + 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange, + 2, int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 3, int_type, uint_type)); + + // OpAtomicStore +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 3, int_type, uint_type)); + + // OpAtomicCompareExchange +#ifndef NDEBUG + ASSERT_DEATH( + fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicCompareExchange, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type)); + + // OpAtomicIIncrement +#ifndef NDEBUG + ASSERT_DEATH( + fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIIncrement, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type)); + +// OpAtomicIDecrement +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2, + int_type, uint_type)); + +// OpAtomicIAdd +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 3, + int_type, uint_type)); + +// OpAtomicISub +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 3, + int_type, uint_type)); + +// OpAtomicSMin +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 3, + int_type, uint_type)); + +// OpAtomicUMin +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 3, + int_type, uint_type)); + +// OpAtomicSMax +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 3, + int_type, uint_type)); + +// OpAtomicUMax +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 3, + int_type, uint_type)); + +// OpAtomicAnd +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 3, + int_type, uint_type)); + +// OpAtomicOr +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 3, + int_type, uint_type)); + +// OpAtomicXor +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 3, + int_type, uint_type)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index cebce8a0..b33dd48c 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -2173,262 +2173,6 @@ TEST(TransformationReplaceIdWithSynonymTest, ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } -// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this -// test so that it covers more atomic operations, and enable the test once the -// issue is fixed. -TEST(TransformationReplaceIdWithSynonymTest, TypesAreCompatible) { - const std::string shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 320 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeInt 32 1 - %9 = OpTypeInt 32 0 - %8 = OpTypeStruct %6 - %10 = OpTypePointer StorageBuffer %8 - %11 = OpVariable %10 StorageBuffer - %86 = OpTypeStruct %9 - %87 = OpTypePointer Workgroup %86 - %88 = OpVariable %87 Workgroup - %89 = OpTypePointer Workgroup %9 - %19 = OpConstant %9 0 - %18 = OpConstant %9 1 - %12 = OpConstant %6 0 - %13 = OpTypePointer StorageBuffer %6 - %15 = OpConstant %6 2 - %16 = OpConstant %6 7 - %20 = OpConstant %9 64 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %14 = OpAccessChain %13 %11 %12 - %90 = OpAccessChain %89 %88 %19 - %21 = OpAtomicLoad %6 %14 %15 %20 - %22 = OpAtomicExchange %6 %14 %15 %20 %16 - %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 - %24 = OpAtomicIIncrement %6 %14 %15 %20 - %25 = OpAtomicIDecrement %6 %14 %15 %20 - %26 = OpAtomicIAdd %6 %14 %15 %20 %16 - %27 = OpAtomicISub %6 %14 %15 %20 %16 - %28 = OpAtomicSMin %6 %14 %15 %20 %16 - %29 = OpAtomicUMin %9 %90 %15 %20 %18 - %30 = OpAtomicSMax %6 %14 %15 %20 %15 - %31 = OpAtomicUMax %9 %90 %15 %20 %18 - %32 = OpAtomicAnd %6 %14 %15 %20 %16 - %33 = OpAtomicOr %6 %14 %15 %20 %16 - %34 = OpAtomicXor %6 %14 %15 %20 %16 - OpAtomicStore %14 %15 %20 %16 - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - spvtools::ValidatorOptions validator_options; - ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, - kConsoleMessageConsumer)); - - const uint32_t int_type = 6; // The id of OpTypeInt 32 1 - const uint32_t uint_type = 9; // The id of OpTypeInt 32 0 - - // OpAtomicLoad -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicLoad, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicLoad, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicLoad, 2, int_type, uint_type)); - - // OpAtomicExchange -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 3, int_type, uint_type)); - - // OpAtomicStore -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicStore, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicStore, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicStore, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicStore, 3, int_type, uint_type)); - - // OpAtomicCompareExchange -#ifndef NDEBUG - ASSERT_DEATH( - TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicCompareExchange, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type)); - - // OpAtomicIIncrement -#ifndef NDEBUG - ASSERT_DEATH( - TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicIIncrement, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type)); - -// OpAtomicIDecrement -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicStore, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicStore, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicStore, 2, int_type, uint_type)); - -// OpAtomicIAdd -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicIAdd, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicIAdd, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicIAdd, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicIAdd, 3, int_type, uint_type)); - -// OpAtomicISub -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicISub, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicISub, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicISub, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicISub, 3, int_type, uint_type)); - -// OpAtomicSMin -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMin, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMin, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMin, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMin, 3, int_type, uint_type)); - -// OpAtomicUMin -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMin, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMin, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMin, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMin, 3, int_type, uint_type)); - -// OpAtomicSMax -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMax, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMax, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMax, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicSMax, 3, int_type, uint_type)); - -// OpAtomicUMax -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMax, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMax, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMax, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicUMax, 3, int_type, uint_type)); - -// OpAtomicAnd -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicAnd, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicAnd, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicAnd, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicAnd, 3, int_type, uint_type)); - -// OpAtomicOr -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicOr, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicOr, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicOr, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicOr, 3, int_type, uint_type)); - -// OpAtomicXor -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicXor, 0, int_type, uint_type), - "Signedness check should not occur on a pointer operand."); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicXor, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicXor, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicXor, 3, int_type, uint_type)); -} - } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_wrap_vector_synonym_test.cpp b/test/fuzz/transformation_wrap_vector_synonym_test.cpp index dae78a5d..0d1009b3 100644 --- a/test/fuzz/transformation_wrap_vector_synonym_test.cpp +++ b/test/fuzz/transformation_wrap_vector_synonym_test.cpp @@ -1389,6 +1389,165 @@ TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationWrapVectorSynonym, DifferentVectorSignedness) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %14 = OpTypeInt 32 0 + %15 = OpTypeVector %14 2 + %18 = OpConstant %14 3 + %19 = OpConstant %14 0 + %20 = OpConstantComposite %15 %18 %19 + %21 = OpConstantComposite %15 %19 %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpIAdd %14 %10 %18 + %101 = OpIAdd %6 %10 %18 + %102 = OpIAdd %6 %18 %19 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(18, {}), MakeDataDescriptor(20, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(19, {}), MakeDataDescriptor(21, {0})); + + { + TransformationWrapVectorSynonym transformation1(100, 12, 20, 200, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(200, {0}), MakeDataDescriptor(100, {}))); + } + + { + TransformationWrapVectorSynonym transformation2(101, 12, 20, 201, 0); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {0}), MakeDataDescriptor(101, {}))); + } + + { + TransformationWrapVectorSynonym transformation3(102, 20, 21, 202, 0); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(202, {0}), MakeDataDescriptor(102, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %14 = OpTypeInt 32 0 + %15 = OpTypeVector %14 2 + %18 = OpConstant %14 3 + %19 = OpConstant %14 0 + %20 = OpConstantComposite %15 %18 %19 + %21 = OpConstantComposite %15 %19 %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %200 = OpIAdd %15 %12 %20 + %100 = OpIAdd %14 %10 %18 + %201 = OpIAdd %7 %12 %20 + %101 = OpIAdd %6 %10 %18 + %202 = OpIAdd %7 %20 %21 + %102 = OpIAdd %6 %18 %19 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, SignednessDoesNotMatchResultType) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpConstantComposite %7 %11 %10 + %14 = OpTypeInt 32 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpIAdd %14 %10 %11 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(13, {0})); + + ASSERT_FALSE(TransformationWrapVectorSynonym(100, 12, 13, 200, 0) + .IsApplicable(context.get(), transformation_context)); +} + } // namespace } // namespace fuzz } // namespace spvtools -- cgit v1.2.3 From 4ac8e5e541ea992dc6f44a4d4eb065a8fe0888ec Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Wed, 15 Sep 2021 12:38:34 -0600 Subject: Add preserve_interface mode to aggressive_dead_code_elim (#4520) This mode is needed by GPU-assisted validation instrumentation which cannot change the shader entry point interface. --- include/spirv-tools/optimizer.hpp | 9 +++++-- source/opt/aggressive_dead_code_elim_pass.cpp | 8 +++--- source/opt/aggressive_dead_code_elim_pass.h | 10 ++++++- source/opt/optimizer.cpp | 12 ++++----- test/opt/aggressive_dead_code_elim_test.cpp | 38 +++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 8141ec86..42eb6442 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -29,7 +29,7 @@ namespace spvtools { namespace opt { class Pass; struct DescriptorSetAndBinding; -} +} // namespace opt // C++ interface for SPIR-V optimization functionalities. It wraps the context // (including target environment and the corresponding SPIR-V grammar) and @@ -514,7 +514,12 @@ Optimizer::PassToken CreateDeadInsertElimPass(); // Conversion, which tends to cause cycles of dead code to be left after // Store/Load elimination passes are completed. These cycles cannot be // eliminated with standard dead code elimination. -Optimizer::PassToken CreateAggressiveDCEPass(); +// +// If |preserve_interface| is true, all non-io variables in the entry point +// interface are considered live and are not eliminated. This mode is needed +// by GPU-Assisted validation instrumentation, where a change in the interface +// is not allowed. +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface = false); // Creates a remove-unused-interface-variables pass. // Removes variables referenced on the |OpEntryPoint| instruction that are not diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index a55e221a..d68c2674 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -607,7 +607,8 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // Keep all entry points. for (auto& entry : get_module()->entry_points()) { - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && + !preserve_interface_) { // In SPIR-V 1.4 and later, entry points must list all global variables // used. DCE can still remove non-input/output variables and update the // interface list. Mark the entry point as live and inputs and outputs as @@ -913,7 +914,8 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } } - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && + !preserve_interface_) { // Remove the dead interface variables from the entry point interface list. for (auto& entry : get_module()->entry_points()) { std::vector new_operands; @@ -939,8 +941,6 @@ bool AggressiveDCEPass::ProcessGlobalValues() { return modified; } -AggressiveDCEPass::AggressiveDCEPass() = default; - Pass::Status AggressiveDCEPass::Process() { // Initialize extensions allowlist InitExtensions(); diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index f02e729f..abc35895 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h @@ -44,7 +44,9 @@ class AggressiveDCEPass : public MemPass { using GetBlocksFunction = std::function*(const BasicBlock*)>; - AggressiveDCEPass(); + AggressiveDCEPass(bool preserve_interface = false) + : preserve_interface_(preserve_interface) {} + const char* name() const override { return "eliminate-dead-code-aggressive"; } Status Process() override; @@ -55,6 +57,12 @@ class AggressiveDCEPass : public MemPass { } private: + // Preserve entry point interface if true. All variables in interface + // will be marked live and will not be eliminated. This mode is needed by + // GPU-Assisted Validation instrumentation where a change in the interface + // is not allowed. + bool preserve_interface_; + // Return true if |varId| is a variable of |storageClass|. |varId| must either // be 0 or the result of an instruction. bool IsVarOfStorage(uint32_t varId, uint32_t storageClass); diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 32f953bb..990be2e4 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -417,22 +417,22 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-desc-idx-check") { RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-buff-oob-check") { RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-buff-addr-check") { RegisterPass(CreateInstBuffAddrCheckPass(7, 23)); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "convert-relaxed-to-half") { RegisterPass(CreateConvertRelaxedToHalfPass()); } else if (pass_name == "relax-float-ops") { @@ -762,9 +762,9 @@ Optimizer::PassToken CreateLocalMultiStoreElimPass() { MakeUnique()); } -Optimizer::PassToken CreateAggressiveDCEPass() { +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) { return MakeUnique( - MakeUnique()); + MakeUnique(preserve_interface)); } Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() { diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index d2581ead..6f79991e 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -7689,6 +7689,44 @@ OpFunctionEnd EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); EXPECT_EQ(text, std::get<0>(result)); } + +TEST_F(AggressiveDCETest, PreserveInterface) { + // Set preserve_interface to true. Verify that unused uniform + // constant in entry point interface is not eliminated. + const std::string text = R"(OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationNV %2 "main" %3 %4 +OpDecorate %3 Location 0 +OpDecorate %4 DescriptorSet 2 +OpDecorate %4 Binding 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%float = OpTypeFloat 32 +%_ptr_CallableDataNV_float = OpTypePointer CallableDataNV %float +%3 = OpVariable %_ptr_CallableDataNV_float CallableDataNV +%13 = OpTypeAccelerationStructureKHR +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%4 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%2 = OpFunction %void None %6 +%15 = OpLabel +OpExecuteCallableKHR %uint_0 %3 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false, + /* preserve_interface */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // Check that logical addressing required -- cgit v1.2.3 From 1454c95d1b834a47403ef3806ffe77c9ebbfa40b Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Wed, 15 Sep 2021 12:38:53 -0600 Subject: spirv-opt: Switch from Vulkan.DebugInfo to Shader.DebugInfo (#4493) Includes: - Shift to use of spirv-header extinst.nonsemantic.shader grammar.json - Remove extinst.nonsemantic.vulkan.debuginfo.100.grammar.json - Enable all optimizations for Shader.DebugInfo Also fixes scalar replacement to only insert DebugValue after all OpVariables. This is not necessary for OpenCL.DebugInfo, but it is for Shader.DebugInfo. Likewise, fixes Private-to-Local to insert DebugDeclare after all OpVariables. Also fixes inlining to handle FunctionDefinition which can show up after first block if early return processing happens. Co-authored-by: baldurk --- Android.mk | 20 +- BUILD.bazel | 11 +- BUILD.gn | 8 +- build_defs.bzl | 24 +- include/spirv-tools/libspirv.h | 2 +- source/CMakeLists.txt | 9 +- source/common_debug_info.h | 4 +- source/ext_inst.cpp | 16 +- ...t.nonsemantic.vulkan.debuginfo.100.grammar.json | 638 --------------------- source/operand.cpp | 4 +- source/opt/aggressive_dead_code_elim_pass.cpp | 14 + source/opt/debug_info_manager.cpp | 50 +- source/opt/debug_info_manager.h | 2 +- source/opt/feature_manager.cpp | 2 +- source/opt/feature_manager.h | 2 +- source/opt/inline_pass.cpp | 9 +- source/opt/instruction.cpp | 13 +- source/opt/instruction.h | 21 +- source/opt/ir_context.cpp | 2 +- source/opt/ir_loader.cpp | 19 +- source/opt/local_access_chain_convert_pass.cpp | 15 + source/opt/local_single_block_elim_pass.cpp | 15 + source/opt/local_single_store_elim_pass.cpp | 15 + source/opt/module.h | 2 +- source/opt/scalar_replacement_pass.cpp | 6 +- source/val/validate_adjacency.cpp | 2 +- source/val/validate_extensions.cpp | 16 +- source/val/validate_layout.cpp | 34 +- test/opt/aggressive_dead_code_elim_test.cpp | 162 ++++++ test/opt/local_ssa_elim_test.cpp | 134 +++++ test/opt/scalar_replacement_test.cpp | 14 +- test/val/val_ext_inst_debug_test.cpp | 120 ++-- 32 files changed, 547 insertions(+), 858 deletions(-) delete mode 100644 source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json diff --git a/Android.mk b/Android.mk index 2bd72ae3..b616654e 100644 --- a/Android.mk +++ b/Android.mk @@ -184,7 +184,7 @@ SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.glsl.st SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.std.100.grammar.json SPV_DEBUGINFO_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.debuginfo.grammar.json SPV_CLDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json -SPV_VKDEBUGINFO100_GRAMMAR=$(LOCAL_PATH)/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json +SPV_VKDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json define gen_spvtools_grammar_tables $(call generate-file-dir,$(1)/core.insts-unified1.inc) @@ -216,7 +216,7 @@ $(LOCAL_PATH)/source/ext_inst.cpp: \ $(1)/opencl.std.insts.inc \ $(1)/debuginfo.insts.inc \ $(1)/opencl.debuginfo.100.insts.inc \ - $(1)/nonsemantic.vulkan.debuginfo.100.insts.inc \ + $(1)/nonsemantic.shader.debuginfo.100.insts.inc \ $(1)/spv-amd-gcn-shader.insts.inc \ $(1)/spv-amd-shader-ballot.insts.inc \ $(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \ @@ -246,7 +246,7 @@ endef # We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100 $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR))) $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR))) -$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticVulkanDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR))) +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR))) define gen_spvtools_vendor_tables @@ -261,22 +261,10 @@ $(1)/$(2).insts.inc : \ @echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar" $(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc endef -define gen_spvtools_vendor_tables_local -$(call generate-file-dir,$(1)/$(2).insts.inc) -$(1)/$(2).insts.inc : \ - $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - $(LOCAL_PATH)/source/extinst.$(2).grammar.json - @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - --extinst-vendor-grammar=$(LOCAL_PATH)/source/extinst.$(2).grammar.json \ - --vendor-insts-output=$(1)/$(2).insts.inc \ - --vendor-operand-kind-prefix=$(3) - @echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar" -$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc -endef # Vendor and debug extended instruction sets, with grammars from SPIRV-Tools source tree. $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),debuginfo,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),opencl.debuginfo.100,"CLDEBUG100_")) -$(eval $(call gen_spvtools_vendor_tables_local,$(SPVTOOLS_OUT_PATH),nonsemantic.vulkan.debuginfo.100,"VKDEBUG100_")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.shader.debuginfo.100,"SHDEBUG100_")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-gcn-shader,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-ballot,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,"")) diff --git a/BUILD.bazel b/BUILD.bazel index 68e612ac..b2031ded 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -3,7 +3,7 @@ load( "COMMON_COPTS", "DEBUGINFO_GRAMMAR_JSON_FILE", "CLDEBUGINFO100_GRAMMAR_JSON_FILE", - "VKDEBUGINFO100_GRAMMAR_JSON_FILE", + "SHDEBUGINFO100_GRAMMAR_JSON_FILE", "TEST_COPTS", "base_test", "generate_core_tables", @@ -12,7 +12,6 @@ load( "generate_glsl_tables", "generate_opencl_tables", "generate_vendor_tables", - "generate_vendor_tables_local", "link_test", "lint_test", "opt_test", @@ -62,7 +61,7 @@ generate_vendor_tables("debuginfo") generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_") -generate_vendor_tables_local("nonsemantic.vulkan.debuginfo.100", "VKDEBUG100_") +generate_vendor_tables("nonsemantic.shader.debuginfo.100", "SHDEBUG100_") generate_vendor_tables("nonsemantic.clspvreflection") @@ -70,7 +69,7 @@ generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE) generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE) -generate_extinst_lang_headers("NonSemanticVulkanDebugInfo100", VKDEBUGINFO100_GRAMMAR_JSON_FILE) +generate_extinst_lang_headers("NonSemanticShaderDebugInfo100", SHDEBUGINFO100_GRAMMAR_JSON_FILE) py_binary( name = "generate_registry_tables", @@ -108,14 +107,14 @@ cc_library( ":gen_enum_string_mapping", ":gen_extinst_lang_headers_DebugInfo", ":gen_extinst_lang_headers_OpenCLDebugInfo100", - ":gen_extinst_lang_headers_NonSemanticVulkanDebugInfo100", + ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100", ":gen_glsl_tables_unified1", ":gen_opencl_tables_unified1", ":gen_registry_tables", ":gen_vendor_tables_debuginfo", ":gen_vendor_tables_nonsemantic_clspvreflection", ":gen_vendor_tables_opencl_debuginfo_100", - ":gen_vendor_tables_nonsemantic_vulkan_debuginfo_100", + ":gen_vendor_tables_nonsemantic_shader_debuginfo_100", ":gen_vendor_tables_spv_amd_gcn_shader", ":gen_vendor_tables_spv_amd_shader_ballot", ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter", diff --git a/BUILD.gn b/BUILD.gn index 20fdeedf..a1da931e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -300,8 +300,8 @@ spvtools_language_header("cldebuginfo100") { grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" } spvtools_language_header("vkdebuginfo100") { - name = "NonSemanticVulkanDebugInfo100" - grammar_file = "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json" + name = "NonSemanticShaderDebugInfo100" + grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json" } spvtools_vendor_tables = [ @@ -336,8 +336,8 @@ spvtools_vendor_tables = [ ] spvtools_vendor_tables_local = [ [ - "nonsemantic.vulkan.debuginfo.100", - "VKDEBUG100_", + "nonsemantic.shader.debuginfo.100", + "SHDEBUG100_", ] ] foreach(table_def, spvtools_vendor_tables) { diff --git a/build_defs.bzl b/build_defs.bzl index 519fa193..b2cd41b9 100644 --- a/build_defs.bzl +++ b/build_defs.bzl @@ -41,7 +41,7 @@ TEST_COPTS = COMMON_COPTS + select({ DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1" CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1" -VKDEBUGINFO100_GRAMMAR_JSON_FILE = "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json" +SHDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1" def generate_core_tables(version = None): if not version: @@ -165,28 +165,6 @@ def generate_vendor_tables(extension, operand_kind_prefix = ""): visibility = ["//visibility:private"], ) -def generate_vendor_tables_local(extension, operand_kind_prefix = ""): - if not extension: - fail("Must specify extension", "extension") - extension_rule = extension.replace("-", "_").replace(".", "_") - grammars = ["source/extinst.{}.grammar.json".format(extension)] - outs = ["{}.insts.inc".format(extension)] - prefices = [operand_kind_prefix] - fmtargs = grammars + outs + prefices - native.genrule( - name = "gen_vendor_tables_" + extension_rule, - srcs = grammars, - outs = outs, - cmd = ( - "$(location :generate_grammar_tables) " + - "--extinst-vendor-grammar=$(location {0}) " + - "--vendor-insts-output=$(location {1}) " + - "--vendor-operand-kind-prefix={2}" - ).format(*fmtargs), - tools = [":generate_grammar_tables"], - visibility = ["//visibility:private"], - ) - def generate_extinst_lang_headers(name, grammar = None): if not grammar: fail("Must specify grammar", "grammar") diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 1cd40cc8..8a06db68 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -309,7 +309,7 @@ typedef enum spv_ext_inst_type_t { SPV_EXT_INST_TYPE_DEBUGINFO, SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100, + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, // Multiple distinct extended instruction set types could return this // value, if they are prefixed with NonSemantic. and are otherwise diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 6530f060..898fcfd3 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -20,10 +20,7 @@ set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_lang # Pull in grammar files that have migrated to SPIRV-Headers set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json") set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json") - -# For now, assume the NonSemantic.Vulkan.DebugInfo grammar file is in the current directory. -# It will later migrate to SPIRV-Headers. -set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json") +set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json") # macro() definitions are used in the following because we need to append .inc # file paths into some global lists (*_CPP_DEPENDS). And those global lists are @@ -154,11 +151,11 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") spvtools_vendor_tables("debuginfo" "debuginfo" "") spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") -spvtools_vendor_tables("nonsemantic.vulkan.debuginfo.100" "vkdi100" "VKDEBUG100_") +spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_") spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) -spvtools_extinst_lang_headers("NonSemanticVulkanDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) spvtools_vimsyntax("unified1" "1.0") add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) diff --git a/source/common_debug_info.h b/source/common_debug_info.h index 0ae85aa0..ffa5d340 100644 --- a/source/common_debug_info.h +++ b/source/common_debug_info.h @@ -18,8 +18,8 @@ #define SOURCE_COMMON_DEBUG_INFO_HEADER_H_ // This enum defines the known common set of instructions that are the same -// between OpenCL.DebugInfo.100 and NonSemantic.Vulkan.DebugInfo.100. -// note that NonSemantic.DebugInfo.100 instructions can still have slightly +// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.100. +// Note that NonSemantic.Shader.* instructions can still have slightly // different encoding, as it does not use literals anywhere and only constants. enum CommonDebugInfoInstructions { CommonDebugInfoDebugInfoNone = 0, diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 8246c204..812053ec 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -29,7 +29,7 @@ #include "debuginfo.insts.inc" #include "glsl.std.450.insts.inc" #include "nonsemantic.clspvreflection.insts.inc" -#include "nonsemantic.vulkan.debuginfo.100.insts.inc" +#include "nonsemantic.shader.debuginfo.100.insts.inc" #include "opencl.debuginfo.100.insts.inc" #include "opencl.std.insts.inc" @@ -56,9 +56,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = { debuginfo_entries}, {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, - {SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100, - ARRAY_SIZE(nonsemantic_vulkan_debuginfo_100_entries), - nonsemantic_vulkan_debuginfo_100_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, + ARRAY_SIZE(nonsemantic_shader_debuginfo_100_entries), + nonsemantic_shader_debuginfo_100_entries}, {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, ARRAY_SIZE(nonsemantic_clspvreflection_entries), nonsemantic_clspvreflection_entries}, @@ -130,8 +130,8 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { if (!strcmp("OpenCL.DebugInfo.100", name)) { return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; } - if (!strcmp("NonSemantic.Vulkan.DebugInfo.100", name)) { - return SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100; + if (!strcmp("NonSemantic.Shader.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; } if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; @@ -146,7 +146,7 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || - type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { return true; } @@ -155,7 +155,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || - type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_DEBUGINFO) { return true; } diff --git a/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json b/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json deleted file mode 100644 index 1d7914d7..00000000 --- a/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json +++ /dev/null @@ -1,638 +0,0 @@ -{ - "copyright" : [ - "Copyright (c) 2018 The Khronos Group Inc.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and/or associated documentation files (the \"Materials\"),", - "to deal in the Materials without restriction, including without limitation", - "the rights to use, copy, modify, merge, publish, distribute, sublicense,", - "and/or sell copies of the Materials, and to permit persons to whom the", - "Materials are furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Materials.", - "", - "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", - "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", - "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", - "", - "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", - "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", - "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", - "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", - "IN THE MATERIALS." - ], - "version" : 100, - "revision" : 2, - "instructions" : [ - { - "opname" : "DebugInfoNone", - "opcode" : 0 - }, - { - "opname" : "DebugCompilationUnit", - "opcode" : 1, - "operands" : [ - { "kind" : "IdRef", "name" : "'Version'" }, - { "kind" : "IdRef", "name" : "'DWARF Version'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Language'" } - ] - }, - { - "opname" : "DebugTypeBasic", - "opcode" : 2, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Size'" }, - { "kind" : "IdRef", "name" : "'Encoding'" }, - { "kind" : "IdRef", "name" : "'Flags'" } - ] - }, - { - "opname" : "DebugTypePointer", - "opcode" : 3, - "operands" : [ - { "kind" : "IdRef", "name" : "'Base Type'" }, - { "kind" : "IdRef", "name" : "'Storage Class'" }, - { "kind" : "IdRef", "name" : "'Flags'" } - ] - }, - { - "opname" : "DebugTypeQualifier", - "opcode" : 4, - "operands" : [ - { "kind" : "IdRef", "name" : "'Base Type'" }, - { "kind" : "IdRef", "name" : "'Type Qualifier'" } - ] - }, - { - "opname" : "DebugTypeArray", - "opcode" : 5, - "operands" : [ - { "kind" : "IdRef", "name" : "'Base Type'" }, - { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugTypeVector", - "opcode" : 6, - "operands" : [ - { "kind" : "IdRef", "name" : "'Base Type'" }, - { "kind" : "IdRef", "name" : "'Component Count'" } - ] - }, - { - "opname" : "DebugTypedef", - "opcode" : 7, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Base Type'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" } - ] - }, - { - "opname" : "DebugTypeFunction", - "opcode" : 8, - "operands" : [ - { "kind" : "IdRef", "name" : "'Flags'" }, - { "kind" : "IdRef", "name" : "'Return Type'" }, - { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugTypeEnum", - "opcode" : 9, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Underlying Type'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Size'" }, - { "kind" : "IdRef", "name" : "'Flags'" }, - { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugTypeComposite", - "opcode" : 10, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Tag'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Linkage Name'" }, - { "kind" : "IdRef", "name" : "'Size'" }, - { "kind" : "IdRef", "name" : "'Flags'" }, - { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugTypeMember", - "opcode" : 11, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Type'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Offset'" }, - { "kind" : "IdRef", "name" : "'Size'" }, - { "kind" : "IdRef", "name" : "'Flags'" }, - { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugTypeInheritance", - "opcode" : 12, - "operands" : [ - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Offset'" }, - { "kind" : "IdRef", "name" : "'Size'" }, - { "kind" : "IdRef", "name" : "'Flags'" } - ] - }, - { - "opname" : "DebugTypePtrToMember", - "opcode" : 13, - "operands" : [ - { "kind" : "IdRef", "name" : "'Member Type'" }, - { "kind" : "IdRef", "name" : "'Parent'" } - ] - }, - { - "opname" : "DebugTypeTemplate", - "opcode" : 14, - "operands" : [ - { "kind" : "IdRef", "name" : "'Target'" }, - { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugTypeTemplateParameter", - "opcode" : 15, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Actual Type'" }, - { "kind" : "IdRef", "name" : "'Value'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" } - ] - }, - { - "opname" : "DebugTypeTemplateTemplateParameter", - "opcode" : 16, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Template Name'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" } - ] - }, - { - "opname" : "DebugTypeTemplateParameterPack", - "opcode" : 17, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugGlobalVariable", - "opcode" : 18, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Type'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Linkage Name'" }, - { "kind" : "IdRef", "name" : "'Variable'" }, - { "kind" : "IdRef", "name" : "'Flags'" }, - { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugFunctionDeclaration", - "opcode" : 19, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Type'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Linkage Name'" }, - { "kind" : "IdRef", "name" : "'Flags'" } - ] - }, - { - "opname" : "DebugFunction", - "opcode" : 20, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Type'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Linkage Name'" }, - { "kind" : "IdRef", "name" : "'Flags'" }, - { "kind" : "IdRef", "name" : "'Scope Line'" }, - { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugLexicalBlock", - "opcode" : 21, - "operands" : [ - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugLexicalBlockDiscriminator", - "opcode" : 22, - "operands" : [ - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Discriminator'" }, - { "kind" : "IdRef", "name" : "'Parent'" } - ] - }, - { - "opname" : "DebugScope", - "opcode" : 23, - "operands" : [ - { "kind" : "IdRef", "name" : "'Scope'" }, - { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugNoScope", - "opcode" : 24 - }, - { - "opname" : "DebugInlinedAt", - "opcode" : 25, - "operands" : [ - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Scope'" }, - { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugLocalVariable", - "opcode" : 26, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Type'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" }, - { "kind" : "IdRef", "name" : "'Flags'" }, - { "kind" : "IdRef", "name" : "'Arg Number'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugInlinedVariable", - "opcode" : 27, - "operands" : [ - { "kind" : "IdRef", "name" : "'Variable'" }, - { "kind" : "IdRef", "name" : "'Inlined'" } - ] - }, - { - "opname" : "DebugDeclare", - "opcode" : 28, - "operands" : [ - { "kind" : "IdRef", "name" : "'Local Variable'" }, - { "kind" : "IdRef", "name" : "'Variable'" }, - { "kind" : "IdRef", "name" : "'Expression'" } - ] - }, - { - "opname" : "DebugValue", - "opcode" : 29, - "operands" : [ - { "kind" : "IdRef", "name" : "'Local Variable'" }, - { "kind" : "IdRef", "name" : "'Value'" }, - { "kind" : "IdRef", "name" : "'Expression'" }, - { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugOperation", - "opcode" : 30, - "operands" : [ - { "kind" : "IdRef", "name" : "'OpCode'" }, - { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugExpression", - "opcode" : 31, - "operands" : [ - { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } - ] - }, - { - "opname" : "DebugMacroDef", - "opcode" : 32, - "operands" : [ - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugMacroUndef", - "opcode" : 33, - "operands" : [ - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Macro'" } - ] - }, - { - "opname" : "DebugImportedEntity", - "opcode" : 34, - "operands" : [ - { "kind" : "IdRef", "name" : "'Name'" }, - { "kind" : "IdRef", "name" : "'Tag'" }, - { "kind" : "IdRef", "name" : "'Source'" }, - { "kind" : "IdRef", "name" : "'Entity'" }, - { "kind" : "IdRef", "name" : "'Line'" }, - { "kind" : "IdRef", "name" : "'Column'" }, - { "kind" : "IdRef", "name" : "'Parent'" } - ] - }, - { - "opname" : "DebugSource", - "opcode" : 35, - "operands" : [ - { "kind" : "IdRef", "name" : "'File'" }, - { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" } - ] - }, - { - "opname" : "DebugFunctionDefinition", - "opcode" : 101, - "operands" : [ - { "kind" : "IdRef", "name" : "'Function'" }, - { "kind" : "IdRef", "name" : "'Definition'" } - ] - } - ], - "operand_kinds" : [ - { - "category" : "BitEnum", - "kind" : "DebugInfoFlags", - "enumerants" : [ - { - "enumerant" : "FlagIsProtected", - "value" : "0x01" - }, - { - "enumerant" : "FlagIsPrivate", - "value" : "0x02" - }, - { - "enumerant" : "FlagIsPublic", - "value" : "0x03" - }, - { - "enumerant" : "FlagIsLocal", - "value" : "0x04" - }, - { - "enumerant" : "FlagIsDefinition", - "value" : "0x08" - }, - { - "enumerant" : "FlagFwdDecl", - "value" : "0x10" - }, - { - "enumerant" : "FlagArtificial", - "value" : "0x20" - }, - { - "enumerant" : "FlagExplicit", - "value" : "0x40" - }, - { - "enumerant" : "FlagPrototyped", - "value" : "0x80" - }, - { - "enumerant" : "FlagObjectPointer", - "value" : "0x100" - }, - { - "enumerant" : "FlagStaticMember", - "value" : "0x200" - }, - { - "enumerant" : "FlagIndirectVariable", - "value" : "0x400" - }, - { - "enumerant" : "FlagLValueReference", - "value" : "0x800" - }, - { - "enumerant" : "FlagRValueReference", - "value" : "0x1000" - }, - { - "enumerant" : "FlagIsOptimized", - "value" : "0x2000" - }, - { - "enumerant" : "FlagIsEnumClass", - "value" : "0x4000" - }, - { - "enumerant" : "FlagTypePassByValue", - "value" : "0x8000" - }, - { - "enumerant" : "FlagTypePassByReference", - "value" : "0x10000" - } - ] - }, - { - "category" : "ValueEnum", - "kind" : "DebugBaseTypeAttributeEncoding", - "enumerants" : [ - { - "enumerant" : "Unspecified", - "value" : "0" - }, - { - "enumerant" : "Address", - "value" : "1" - }, - { - "enumerant" : "Boolean", - "value" : "2" - }, - { - "enumerant" : "Float", - "value" : "3" - }, - { - "enumerant" : "Signed", - "value" : "4" - }, - { - "enumerant" : "SignedChar", - "value" : "5" - }, - { - "enumerant" : "Unsigned", - "value" : "6" - }, - { - "enumerant" : "UnsignedChar", - "value" : "7" - } - ] - }, - { - "category" : "ValueEnum", - "kind" : "DebugCompositeType", - "enumerants" : [ - { - "enumerant" : "Class", - "value" : "0" - }, - { - "enumerant" : "Structure", - "value" : "1" - }, - { - "enumerant" : "Union", - "value" : "2" - } - ] - }, - { - "category" : "ValueEnum", - "kind" : "DebugTypeQualifier", - "enumerants" : [ - { - "enumerant" : "ConstType", - "value" : "0" - }, - { - "enumerant" : "VolatileType", - "value" : "1" - }, - { - "enumerant" : "RestrictType", - "value" : "2" - }, - { - "enumerant" : "AtomicType", - "value" : "3" - } - ] - }, - { - "category" : "ValueEnum", - "kind" : "DebugOperation", - "enumerants" : [ - { - "enumerant" : "Deref", - "value" : "0" - }, - { - "enumerant" : "Plus", - "value" : "1" - }, - { - "enumerant" : "Minus", - "value" : "2" - }, - { - "enumerant" : "PlusUconst", - "value" : "3", - "parameters" : [ - { "kind" : "IdRef" } - ] - }, - { - "enumerant" : "BitPiece", - "value" : "4", - "parameters" : [ - { "kind" : "IdRef" }, - { "kind" : "IdRef" } - ] - }, - { - "enumerant" : "Swap", - "value" : "5" - }, - { - "enumerant" : "Xderef", - "value" : "6" - }, - { - "enumerant" : "StackValue", - "value" : "7" - }, - { - "enumerant" : "Constu", - "value" : "8", - "parameters" : [ - { "kind" : "IdRef" } - ] - }, - { - "enumerant" : "Fragment", - "value" : "9", - "parameters" : [ - { "kind" : "IdRef" }, - { "kind" : "IdRef" } - ] - } - ] - }, - { - "category" : "ValueEnum", - "kind" : "DebugImportedEntity", - "enumerants" : [ - { - "enumerant" : "ImportedModule", - "value" : "0" - }, - { - "enumerant" : "ImportedDeclaration", - "value" : "1" - } - ] - } - ] -} diff --git a/source/operand.cpp b/source/operand.cpp index bff36a26..6d83e81e 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -579,8 +579,8 @@ std::function spvOperandCanBeForwardDeclaredFunction( std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( spv_ext_inst_type_t ext_type, uint32_t key) { // The Vulkan debug info extended instruction set is non-semantic so allows no - // forward references ever. - if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + // forward references ever + if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { return [](unsigned) { return false; }; } diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index d68c2674..1173eafe 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -145,6 +145,19 @@ bool AggressiveDCEPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // Only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const char* extension_name = + reinterpret_cast(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -1002,6 +1015,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index 2e8e1327..357e1ecc 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -18,7 +18,7 @@ #include "source/opt/ir_context.h" -// Constants for OpenCL.DebugInfo.100 & NonSemantic.Vulkan.DebugInfo.100 +// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100 // extension instructions. static const uint32_t kOpLineOperandLineIndex = 1; @@ -117,14 +117,14 @@ void DebugInfoManager::RegisterDbgFunction(Instruction* inst) { fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && "Register DebugFunction for a function that already has DebugFunction"); fn_id_to_dbg_fn_[fn_id] = inst; - } else if (inst->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + } else if (inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { auto fn_id = inst->GetSingleWordOperand( kDebugFunctionDefinitionOperandOpFunctionIndex); auto fn_inst = GetDbgInst(inst->GetSingleWordOperand( kDebugFunctionDefinitionOperandDebugFunctionIndex)); - assert(fn_inst && fn_inst->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugFunction); + assert(fn_inst && fn_inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunction); assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && "Register DebugFunctionDefinition for a function that already has " "DebugFunctionDefinition"); @@ -155,7 +155,7 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, spv_operand_type_t line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER; - // In NonSemantic.Vulkan.DebugInfo.100, all constants are IDs of OpConstant, + // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant, // not literals. if (setId == context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) @@ -307,7 +307,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { })); } else { uint32_t deref_id = context()->get_constant_mgr()->GetUIntConst( - NonSemanticVulkanDebugInfo100Deref); + NonSemanticShaderDebugInfo100Deref); deref_operation = std::unique_ptr( new Instruction(context(), SpvOpExtInst, @@ -316,7 +316,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {static_cast( - NonSemanticVulkanDebugInfo100DebugOperation)}}, + NonSemanticShaderDebugInfo100DebugOperation)}}, {SPV_OPERAND_TYPE_ID, {deref_id}}, })); } @@ -574,8 +574,8 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( } uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) { - assert(inst->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugOperation && + assert(inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation && "inst must be Vulkan DebugOperation"); return context() ->get_constant_mgr() @@ -606,7 +606,7 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( } } else { uint32_t operation_const = GetVulkanDebugOperation(operation); - if (operation_const != NonSemanticVulkanDebugInfo100Deref) { + if (operation_const != NonSemanticShaderDebugInfo100Deref) { return 0; } } @@ -682,8 +682,8 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { RegisterDbgInst(inst); if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction || - inst->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { RegisterDbgFunction(inst); } @@ -695,10 +695,10 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { } if (deref_operation_ == nullptr && - inst->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugOperation) { + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { uint32_t operation_const = GetVulkanDebugOperation(inst); - if (operation_const == NonSemanticVulkanDebugInfo100Deref) { + if (operation_const == NonSemanticShaderDebugInfo100Deref) { deref_operation_ = inst; } } @@ -759,8 +759,11 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable( {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetEmptyDebugExpression()->result_id()}}, })); - auto* added_dbg_decl = - local_var->NextNode()->InsertBefore(std::move(new_dbg_decl)); + // Must insert after all OpVariables in block + Instruction* insert_before = local_var; + while (insert_before->opcode() == SpvOpVariable) + insert_before = insert_before->NextNode(); + auto* added_dbg_decl = insert_before->InsertBefore(std::move(new_dbg_decl)); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl); if (context()->AreAnalysesValid( @@ -818,8 +821,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); } - if (instr->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugFunction) { + if (instr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunction) { auto fn_id = instr->GetSingleWordOperand( kDebugFunctionDefinitionOperandOpFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); @@ -851,11 +854,10 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { deref_operation_ = &*dbg_instr_itr; break; } else if (instr != &*dbg_instr_itr && - dbg_instr_itr->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugOperation) { + dbg_instr_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr); - - if (operation_const == NonSemanticVulkanDebugInfo100Deref) { + if (operation_const == NonSemanticShaderDebugInfo100Deref) { deref_operation_ = &*dbg_instr_itr; break; } diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h index 679ae138..df34b30f 100644 --- a/source/opt/debug_info_manager.h +++ b/source/opt/debug_info_manager.h @@ -68,7 +68,7 @@ class DebugInlinedAtContext { }; // A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and -// NonSemantic.Vulkan.DebugInfo.100 extension instructions. +// NonSemantic.Shader.DebugInfo.100 extension instructions. class DebugInfoManager { public: // Constructs a debug information manager from the given |context|. diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index db3abddf..e00eaac9 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -81,7 +81,7 @@ void FeatureManager::AddExtInstImportIds(Module* module) { extinst_importid_OpenCL100DebugInfo_ = module->GetExtInstImportId("OpenCL.DebugInfo.100"); extinst_importid_Vulkan100DebugInfo_ = - module->GetExtInstImportId("NonSemantic.Vulkan.DebugInfo.100"); + module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100"); } bool operator==(const FeatureManager& a, const FeatureManager& b) { diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index 4720c6dc..f66193fd 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h @@ -97,7 +97,7 @@ class FeatureManager { // for performance. uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; - // Common NonSemanticVulkan100DebugInfo external instruction import ids, + // Common NonSemanticShader100DebugInfo external instruction import ids, // cached for performance. uint32_t extinst_importid_Vulkan100DebugInfo_ = 0; }; diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index a6bdaaff..8b91ad6c 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp @@ -405,8 +405,8 @@ bool InlinePass::InlineEntryBlock( while (callee_inst_itr != callee_first_block->end()) { // Don't inline function definition links, the calling function is not a // definition. - if (callee_inst_itr->GetVulkan100DebugOpcode() == - NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + if (callee_inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { ++callee_inst_itr; continue; } @@ -441,6 +441,11 @@ std::unique_ptr InlinePass::InlineBasicBlocks( auto tail_inst_itr = callee_block_itr->end(); for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr; ++inst_itr) { + // Don't inline function definition links, the calling function is not a + // definition + if (inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) + continue; if (!InlineSingleInstruction( callee2caller, new_blk_ptr.get(), &*inst_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 577c0a67..174b74f3 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -16,7 +16,6 @@ #include -#include "NonSemanticVulkanDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/disassemble.h" #include "source/opt/fold.h" @@ -33,7 +32,7 @@ const uint32_t kLoadBaseIndex = 0; const uint32_t kPointerTypeStorageClassIndex = 0; const uint32_t kTypeImageSampledIndex = 5; -// Constants for OpenCL.DebugInfo.100 / NonSemantic.Vulkan.DebugInfo.100 +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 // extension instructions. const uint32_t kExtInstSetIdInIdx = 0; const uint32_t kExtInstInstructionInIdx = 1; @@ -624,22 +623,22 @@ OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { GetSingleWordInOperand(kExtInstInstructionInIdx)); } -NonSemanticVulkanDebugInfo100Instructions Instruction::GetVulkan100DebugOpcode() +NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode() const { if (opcode() != SpvOpExtInst) { - return NonSemanticVulkanDebugInfo100InstructionsMax; + return NonSemanticShaderDebugInfo100InstructionsMax; } if (!context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) { - return NonSemanticVulkanDebugInfo100InstructionsMax; + return NonSemanticShaderDebugInfo100InstructionsMax; } if (GetSingleWordInOperand(kExtInstSetIdInIdx) != context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) { - return NonSemanticVulkanDebugInfo100InstructionsMax; + return NonSemanticShaderDebugInfo100InstructionsMax; } - return NonSemanticVulkanDebugInfo100Instructions( + return NonSemanticShaderDebugInfo100Instructions( GetSingleWordInOperand(kExtInstInstructionInIdx)); } diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 7cdfc44d..5019170e 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -22,7 +22,7 @@ #include #include -#include "NonSemanticVulkanDebugInfo100.h" +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/common_debug_info.h" #include "source/latest_version_glsl_std_450_header.h" @@ -552,13 +552,13 @@ class Instruction : public utils::IntrusiveNodeBase { // OpenCLDebugInfo100InstructionsMax. OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; - // Returns debug opcode of a NonSemantic.Vulkan.DebugInfo.100 instruction. If - // it is not a NonSemantic.Vulkan.DebugInfo.100 instruction, just returns - // NonSemanticVulkanDebugInfo100InstructionsMax. - NonSemanticVulkanDebugInfo100Instructions GetVulkan100DebugOpcode() const; + // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If + // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return + // NonSemanticShaderDebugInfo100InstructionsMax. + NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const; // Returns debug opcode of an OpenCL.100.DebugInfo or - // NonSemantic.Vulkan.DebugInfo.100 instruction. Since these overlap, we + // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we // return the OpenCLDebugInfo code CommonDebugInfoInstructions GetCommonDebugOpcode() const; @@ -566,10 +566,11 @@ class Instruction : public utils::IntrusiveNodeBase { bool IsOpenCL100DebugInstr() const { return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; } - // Returns true if it is a NonSemantic.Vulkan.DebugInfo.100 instruction. - bool IsVulkan100DebugInstr() const { - return GetVulkan100DebugOpcode() != - NonSemanticVulkanDebugInfo100InstructionsMax; + + // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction. + bool IsShader100DebugInstr() const { + return GetShader100DebugOpcode() != + NonSemanticShaderDebugInfo100InstructionsMax; } bool IsCommonDebugInstr() const { return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax; diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 90873461..2b8fe1fb 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -30,7 +30,7 @@ static const int kSpvDecorateBuiltinInIdx = 2; static const int kEntryPointInterfaceInIdx = 3; static const int kEntryPointFunctionIdInIdx = 1; -// Constants for OpenCL.DebugInfo.100 / NonSemantic.Vulkan.DebugInfo.100 +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 // extension instructions. static const uint32_t kDebugFunctionOperandFunctionIndex = 13; static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index bfdd59b3..87718036 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -17,7 +17,6 @@ #include #include "DebugInfo.h" -#include "NonSemanticVulkanDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/ext_inst.h" #include "source/opt/log.h" @@ -56,7 +55,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || inst->ext_inst_type == - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { const CommonDebugInfoInstructions ext_inst_key = CommonDebugInfoInstructions(ext_inst_index); if (ext_inst_key == CommonDebugInfoDebugScope) { @@ -243,15 +242,15 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { } } } else if (inst->ext_inst_type == - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { - const NonSemanticVulkanDebugInfo100Instructions ext_inst_key = - NonSemanticVulkanDebugInfo100Instructions(ext_inst_index); + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); switch (ext_inst_key) { - case NonSemanticVulkanDebugInfo100DebugDeclare: - case NonSemanticVulkanDebugInfo100DebugValue: - case NonSemanticVulkanDebugInfo100DebugScope: - case NonSemanticVulkanDebugInfo100DebugNoScope: - case NonSemanticVulkanDebugInfo100DebugFunctionDefinition: { + case NonSemanticShaderDebugInfo100DebugDeclare: + case NonSemanticShaderDebugInfo100DebugValue: + case NonSemanticShaderDebugInfo100DebugScope: + case NonSemanticShaderDebugInfo100DebugNoScope: + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: { if (block_ == nullptr) { // Inside function but outside blocks Errorf(consumer_, src, loc, "Debug info extension instruction found inside function " diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index e8dbdfc4..da9ba8cc 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -333,6 +333,20 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const char* extension_name = + reinterpret_cast(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -421,6 +435,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index a742e3b1..80b081dc 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -188,6 +188,20 @@ bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const char* extension_name = + reinterpret_cast(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -273,6 +287,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index 9701d10e..a32db3b7 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -53,6 +53,20 @@ bool LocalSingleStoreElimPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const char* extension_name = + reinterpret_cast(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -126,6 +140,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { diff --git a/source/opt/module.h b/source/opt/module.h index 43b55ae1..0360b7d5 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -104,7 +104,7 @@ class Module { inline void AddDebug3Inst(std::unique_ptr d); // Appends a debug info extension (OpenCL.DebugInfo.100, - // NonSemantic.Vulkan.DebugInfo.100, or DebugInfo) instruction to this module. + // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module. inline void AddExtInstDebugInfo(std::unique_ptr d); // Appends an annotation instruction to this module. diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index 8adca7b9..388eb76d 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -172,10 +172,14 @@ bool ScalarReplacementPass::ReplaceWholeDebugDeclare( // Add DebugValue instruction with Indexes operand and Deref operation. int32_t idx = 0; for (const auto* var : replacements) { + Instruction* insert_before = var->NextNode(); + while (insert_before->opcode() == SpvOpVariable) + insert_before = insert_before->NextNode(); + assert(insert_before != nullptr && "unexpected end of list"); Instruction* added_dbg_value = context()->get_debug_info_mgr()->AddDebugValueForDecl( dbg_decl, /*value_id=*/var->result_id(), - /*insert_before=*/var->NextNode(), /*scope_and_line=*/dbg_decl); + /*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl); if (added_dbg_value == nullptr) return false; added_dbg_value->AddOperand( diff --git a/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp index 13720f0e..8e6c373e 100644 --- a/source/val/validate_adjacency.cpp +++ b/source/val/validate_adjacency.cpp @@ -63,7 +63,7 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) { // NOTE: This does not apply to the non-semantic vulkan debug info. if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) || inst.ext_inst_type() == - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { adjacency_status = PHI_AND_VAR_INVALID; } break; diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index 5ea23e1f..dccbe149 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -20,7 +20,7 @@ #include "spirv/unified1/NonSemanticClspvReflection.h" -#include "NonSemanticVulkanDebugInfo100.h" +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/common_debug_info.h" #include "source/diagnostic.h" @@ -98,7 +98,7 @@ spv_result_t ValidateOperandForDebugInfo( return SPV_SUCCESS; } -// For NonSemantic.Vulkan.DebugInfo.100 check that the operand of a debug info +// For NonSemantic.Shader.DebugInfo.100 check that the operand of a debug info // instruction |inst| at |word_index| is a result id of a 32-bit integer // OpConstant instruction. For OpenCL.DebugInfo.100 the parameter is a literal // word so cannot be validated. @@ -140,7 +140,7 @@ bool DoesDebugInfoOperandMatchExpectation( if (debug_inst->opcode() != SpvOpExtInst || (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 && debug_inst->ext_inst_type() != - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) || + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || !expectation(CommonDebugInfoInstructions(debug_inst->word(4)))) { return false; } @@ -706,7 +706,7 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _, const spv_ext_inst_type_t ext_inst_type = spv_ext_inst_type_t(inst->ext_inst_type()); const bool vulkanDebugInfo = - ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100; + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; uint32_t encoding = dbg_type->word(7); if (!vulkanDebugInfo || IsUint32Constant(_, encoding)) { auto ocl_encoding = OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( @@ -2707,7 +2707,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { } } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || ext_inst_type == - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { if (!_.IsVoidType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " @@ -2716,7 +2716,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { } const bool vulkanDebugInfo = - ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100; + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; auto num_words = inst->words().size(); @@ -2965,7 +2965,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); CHECK_CONST_UINT_OPERAND("Line", 8); CHECK_CONST_UINT_OPERAND("Column", 9); - // NonSemantic.Vulkan.DebugInfo doesn't have the Parent operand + // NonSemantic.Shader.DebugInfo doesn't have the Parent operand if (vulkanDebugInfo) { CHECK_OPERAND("Offset", SpvOpConstant, 10); CHECK_OPERAND("Size", SpvOpConstant, 11); @@ -3023,7 +3023,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { CHECK_OPERAND("Linkage Name", SpvOpString, 11); CHECK_CONST_UINT_OPERAND("Flags", 12); CHECK_CONST_UINT_OPERAND("Scope Line", 13); - // NonSemantic.Vulkan.DebugInfo.100 doesn't include a reference to the + // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the // OpFunction if (vulkanDebugInfo) { if (num_words == 15) { diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp index e6f4fcad..90923fce 100644 --- a/source/val/validate_layout.cpp +++ b/source/val/validate_layout.cpp @@ -17,7 +17,7 @@ #include #include "DebugInfo.h" -#include "NonSemanticVulkanDebugInfo100.h" +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/diagnostic.h" #include "source/opcode.h" @@ -51,15 +51,15 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, local_debug_info = true; } } else if (inst->ext_inst_type() == - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { - const NonSemanticVulkanDebugInfo100Instructions ext_inst_key = - NonSemanticVulkanDebugInfo100Instructions(ext_inst_index); - if (ext_inst_key == NonSemanticVulkanDebugInfo100DebugScope || - ext_inst_key == NonSemanticVulkanDebugInfo100DebugNoScope || - ext_inst_key == NonSemanticVulkanDebugInfo100DebugDeclare || - ext_inst_key == NonSemanticVulkanDebugInfo100DebugValue || + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || ext_inst_key == - NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { local_debug_info = true; } } else { @@ -256,15 +256,15 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, local_debug_info = true; } } else if (inst->ext_inst_type() == - SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) { - const NonSemanticVulkanDebugInfo100Instructions ext_inst_key = - NonSemanticVulkanDebugInfo100Instructions(ext_inst_index); - if (ext_inst_key == NonSemanticVulkanDebugInfo100DebugScope || - ext_inst_key == NonSemanticVulkanDebugInfo100DebugNoScope || - ext_inst_key == NonSemanticVulkanDebugInfo100DebugDeclare || - ext_inst_key == NonSemanticVulkanDebugInfo100DebugValue || + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || ext_inst_key == - NonSemanticVulkanDebugInfo100DebugFunctionDefinition) { + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { local_debug_info = true; } } else { diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 6f79991e..e49acf5b 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -7063,6 +7063,168 @@ TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) { SinglePassRunAndMatch(text, true); } +TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) { + // Verify that dead local variable tc and store eliminated but all + // in-function NonSemantic Shader debuginfo kept. + + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %7 = OpString "foo.frag" + %8 = OpString "PS_OUTPUT" + %9 = OpString "float" + %10 = OpString "vColor" + %11 = OpString "PS_INPUT" + %12 = OpString "vTextureCoords" + %13 = OpString "@type.2d.image" + %14 = OpString "type.2d.image" + %15 = OpString "Texture2D.TemplateParam" + %16 = OpString "src.MainPs" + %17 = OpString "tc" + %18 = OpString "ps_output" + %19 = OpString "i" + %20 = OpString "@type.sampler" + %21 = OpString "type.sampler" + %22 = OpString "g_sAniso" + %23 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_7 = OpConstant %uint 7 + %uint_8 = OpConstant %uint 8 + %uint_10 = OpConstant %uint 10 + %uint_11 = OpConstant %uint 11 + %uint_12 = OpConstant %uint 12 + %uint_14 = OpConstant %uint 14 + %uint_15 = OpConstant %uint 15 + %uint_16 = OpConstant %uint 16 + %uint_17 = OpConstant %uint 17 + %uint_19 = OpConstant %uint 19 + %uint_29 = OpConstant %uint 29 + %uint_64 = OpConstant %uint 64 + %45 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %51 = OpExtInst %void %1 DebugInfoNone + %52 = OpExtInst %void %1 DebugExpression + %53 = OpExtInst %void %1 DebugOperation %uint_0 + %54 = OpExtInst %void %1 DebugExpression %53 + %55 = OpExtInst %void %1 DebugSource %7 + %56 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %55 %uint_5 + %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 %uint_3 %uint_0 + %60 = OpExtInst %void %1 DebugTypeVector %59 %uint_4 + %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 %uint_12 %uint_5 %uint_0 %uint_128 %uint_3 + %57 = OpExtInst %void %1 DebugTypeComposite %8 %uint_1 %55 %uint_10 %uint_1 %56 %8 %uint_128 %uint_3 %58 + %63 = OpExtInst %void %1 DebugTypeVector %59 %uint_2 + %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 %uint_7 %uint_5 %uint_0 %uint_64 %uint_3 + %61 = OpExtInst %void %1 DebugTypeComposite %11 %uint_1 %55 %uint_5 %uint_1 %56 %11 %uint_64 %uint_3 %62 + %64 = OpExtInst %void %1 DebugTypeComposite %13 %uint_0 %55 %uint_0 %uint_0 %56 %14 %51 %uint_3 + %67 = OpExtInst %void %1 DebugTypeFunction %uint_3 %57 %61 + %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 %uint_15 %uint_1 %56 %16 %uint_3 %uint_16 + %69 = OpExtInst %void %1 DebugLexicalBlock %55 %uint_16 %uint_1 %68 + %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 %uint_19 %uint_12 %69 %uint_4 + %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 %uint_17 %uint_15 %69 %uint_4 + %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 %uint_15 %uint_29 %68 %uint_4 %uint_1 + %73 = OpExtInst %void %1 DebugTypeComposite %20 %uint_1 %55 %uint_0 %uint_0 %56 %21 %51 %uint_3 + %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 %uint_3 %uint_14 %56 %22 %g_sAniso %uint_8 + %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 %uint_1 %uint_11 %56 %23 %g_tColor %uint_8 + %MainPs = OpFunction %void None %45 + %76 = OpLabel + %78 = OpVariable %_ptr_Function_PS_OUTPUT Function + %79 = OpVariable %_ptr_Function_v2float Function + %81 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %82 = OpLoad %v2float %in_var_TEXCOORD2 + %83 = OpCompositeConstruct %PS_INPUT %82 + OpStore %param_var_i %83 + %112 = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs + %109 = OpExtInst %void %1 DebugScope %68 + %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 + %110 = OpExtInst %void %1 DebugScope %69 + %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %68 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52 + %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %89 = OpLoad %v2float %88 + OpStore %79 %89 +;CHECK-NOT: OpStore %79 %89 + %106 = OpExtInst %void %1 DebugValue %70 %89 %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52 + %91 = OpLoad %type_2d_image %g_tColor + %92 = OpLoad %type_sampler %g_sAniso + %94 = OpSampledImage %type_sampled_image %91 %92 + %95 = OpImageSampleImplicitLod %v4float %94 %89 None + %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0 + OpStore %96 %95 + %97 = OpLoad %PS_OUTPUT %78 + OpStore %81 %97 + %111 = OpExtInst %void %1 DebugNoScope +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope + %100 = OpCompositeExtract %v4float %97 0 + OpStore %out_var_SV_Target0 %100 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) { // Verify that local variable tc and its store are kept by DebugDeclare. // diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp index ca9aba33..ae98649f 100644 --- a/test/opt/local_ssa_elim_test.cpp +++ b/test/opt/local_ssa_elim_test.cpp @@ -2165,6 +2165,140 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(LocalSSAElimTest, ShaderDebugForLoop) { + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] + +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0 +; CHECK-NEXT: OpStore %i %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0 + +; CHECK-NOT: DebugDeclare + +; CHECK: [[loop_head:%\w+]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0 +; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]] +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None +; CHECK-NEXT: OpBranch [[loop_body:%\w+]] + +; CHECK-NEXT: [[loop_body]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]] + +; CHECK: [[bb]] = OpLabel +; CHECK: OpStore %f [[f_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]] +; CHECK-NEXT: OpBranch [[loop_cont]] + +; CHECK: [[loop_cont]] = OpLabel +; CHECK: OpStore %i [[i_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]] +; CHECK-NEXT: OpBranch [[loop_head]] + +; CHECK: [[loop_merge]] = OpLabel + +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_10 = OpConstant %uint 10 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5 +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0 +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf %uint_4 +%main_ty = OpExtInst %void %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10 +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(LocalSSAElimTest, AddDebugValueForFunctionParameterWithPhi) { // Test the distribution of DebugValue for a parameter of an inlined function // and the visibility of Phi instruction. The ssa-rewrite pass must add diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp index 8115f5fb..d99399ad 100644 --- a/test/opt/scalar_replacement_test.cpp +++ b/test/opt/scalar_replacement_test.cpp @@ -1935,12 +1935,12 @@ OpName %6 "simple_struct" ; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable ; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 ; CHECK-NOT: DebugDeclare %decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr @@ -2058,10 +2058,10 @@ OpName %6 "simple_struct" ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 +; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_2 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 %int_0 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_1 %int_1 -; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 ; CHECK-NOT: DebugDeclare %decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr @@ -2174,12 +2174,12 @@ OpName %6 "simple_struct" ; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLocalVariable ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3 ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 OpBranch %20 diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp index d3b9f038..307a8009 100644 --- a/test/val/val_ext_inst_debug_test.cpp +++ b/test/val/val_ext_inst_debug_test.cpp @@ -354,7 +354,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugSourceInFunction) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, @@ -436,7 +436,7 @@ TEST_P(ValidateLocalDebugInfoOutOfFunction, VulkanDebugInfo100DebugScope) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string constants = R"( @@ -738,7 +738,7 @@ INSTANTIATE_TEST_SUITE_P(OpenCLAndVkDebugInfo100, ValidateXDebugInfo, )", R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )", })); @@ -798,7 +798,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugCompilationUnitFail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string constants = R"( @@ -872,7 +872,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailName) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -941,7 +941,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailSize) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1107,7 +1107,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifier) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1141,7 +1141,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifierFail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1398,7 +1398,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1436,7 +1436,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayWithVariableSize) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1466,7 +1466,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailBaseType) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1499,7 +1499,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCount) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1534,7 +1534,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1569,7 +1569,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountZero) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1610,7 +1610,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1762,7 +1762,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVector) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1792,7 +1792,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1825,7 +1825,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentZero) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1858,7 +1858,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentFive) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -1967,7 +1967,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypedef) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -2001,7 +2001,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugTypedef, Fail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -2152,7 +2152,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionAndParams) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -2184,7 +2184,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailReturn) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -2219,7 +2219,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailParam) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -2349,7 +2349,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeEnum) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -2383,7 +2383,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugTypeEnum, Fail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -2705,7 +2705,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -2753,7 +2753,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -2822,7 +2822,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -3029,7 +3029,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -3069,7 +3069,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -3133,7 +3133,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -3290,7 +3290,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugLexicalBlock) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -3321,7 +3321,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugLexicalBlock, Fail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -3363,7 +3363,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailScope) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -3394,7 +3394,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailInlinedAt) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -3505,7 +3505,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugLocalVariable) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -3540,7 +3540,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugLocalVariable, Fail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -3736,7 +3736,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugDeclare) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string body = R"( @@ -3753,7 +3753,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugDeclareParam) { CompileSuccessfully(R"( OpCapability Shader OpExtension "SPV_KHR_non_semantic_info" - %1 = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %in_var_COLOR %4 = OpString "test.hlsl" @@ -3843,7 +3843,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugDeclare, Fail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const auto& param = GetParam(); @@ -3913,7 +3913,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugExpression) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, @@ -3929,7 +3929,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugExpressionFail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, @@ -4159,7 +4159,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4198,7 +4198,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4237,7 +4237,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4274,7 +4274,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4315,7 +4315,7 @@ main() {} const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4522,7 +4522,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariable) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4555,7 +4555,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableStaticMember) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4587,7 +4587,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableDebugInfoNone) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4618,7 +4618,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableConst) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( @@ -4652,7 +4652,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugGlobalVariable, Fail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), @@ -4806,7 +4806,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAt) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string body = R"( @@ -4844,7 +4844,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string body = R"( @@ -4883,7 +4883,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail2) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string body = R"( @@ -5042,7 +5042,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugValue) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string body = R"( @@ -5084,7 +5084,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugValueWithVariableIndex) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const std::string body = R"( @@ -5122,7 +5122,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugValue, Fail) { const std::string extension = R"( OpExtension "SPV_KHR_non_semantic_info" -%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; const auto& param = GetParam(); @@ -5153,7 +5153,7 @@ TEST_F(ValidateVulkan100DebugInfo, VulkanDebugInfoSample) { ss << R"( OpCapability Shader OpExtension "SPV_KHR_non_semantic_info" - %id_1 = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100" + %id_1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %id_MainPs "MainPs" %id_in_var_TEXCOORD2 %id_out_var_SV_Target0 OpExecutionMode %id_MainPs OriginUpperLeft -- cgit v1.2.3 From 59f51bb4f8389713ec6136f09441c10c2da62727 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 20 Sep 2021 13:02:47 -0400 Subject: Fix extract with out-of-bounds index (#4529) * Fix extract with out-of-bounds index When folding a OpCompositeExtract that is fed by an OpCompositeConstruct, we handle and out of bounds index, but only in the case where the result of the OpCompostiteConstruct is a struct. This change refactors that folding rule and then improves it to handle an out-of-bounds access when the result of the OpCompositeConstruct is a vector. --- source/opt/folding_rules.cpp | 184 +++++++++++++++++++++++++------------------ test/opt/fold_test.cpp | 14 +++- 2 files changed, 121 insertions(+), 77 deletions(-) diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 20051a6b..886c03fd 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -14,6 +14,7 @@ #include "source/opt/folding_rules.h" +#include #include #include #include @@ -1463,90 +1464,121 @@ FoldingRule IntMultipleBy1() { }; } -FoldingRule CompositeConstructFeedingExtract() { - return [](IRContext* context, Instruction* inst, - const std::vector&) { - // If the input to an OpCompositeExtract is an OpCompositeConstruct, - // then we can simply use the appropriate element in the construction. - assert(inst->opcode() == SpvOpCompositeExtract && - "Wrong opcode. Should be OpCompositeExtract."); - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - analysis::TypeManager* type_mgr = context->get_type_mgr(); - - // If there are no index operands, then this rule cannot do anything. - if (inst->NumInOperands() <= 1) { - return false; - } +// Returns the number of elements that the |index|th in operand in |inst| +// contributes to the result of |inst|. |inst| must be an +// OpCompositeConstructInstruction. +uint32_t GetNumOfElementsContributedByOperand(IRContext* context, + const Instruction* inst, + uint32_t index) { + assert(inst->opcode() == SpvOpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + analysis::Vector* result_type = + type_mgr->GetType(inst->type_id())->AsVector(); + if (result_type == nullptr) { + // If the result of the OpCompositeConstruct is not a vector then every + // operands corresponds to a single element in the result. + return 1; + } - uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); - Instruction* cinst = def_use_mgr->GetDef(cid); + // If the result type is a vector then the operands are either scalars or + // vectors. If it is a scalar, then it corresponds to a single element. If it + // is a vector, then each element in the vector will be an element in the + // result. + uint32_t id = inst->GetSingleWordInOperand(index); + Instruction* def = def_use_mgr->GetDef(id); + analysis::Vector* type = type_mgr->GetType(def->type_id())->AsVector(); + if (type == nullptr) { + return 1; + } + return type->element_count(); +} - if (cinst->opcode() != SpvOpCompositeConstruct) { - return false; - } +// Returns the in-operands for an OpCompositeExtract instruction that are needed +// to extract the |result_index|th element in the result of |inst| without using +// the result of |inst|. Returns the empty vector if |result_index| is +// out-of-bounds. |inst| must be an |OpCompositeConstruct| instruction. +std::vector GetExtractOperandsForElementOfCompositeConstruct( + IRContext* context, const Instruction* inst, uint32_t result_index) { + assert(inst->opcode() == SpvOpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); - std::vector operands; - analysis::Type* composite_type = type_mgr->GetType(cinst->type_id()); - if (composite_type->AsVector() == nullptr) { - // Get the element being extracted from the OpCompositeConstruct - // Since it is not a vector, it is simple to extract the single element. - uint32_t element_index = inst->GetSingleWordInOperand(1); - uint32_t element_id = cinst->GetSingleWordInOperand(element_index); - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); - - // Add the remaining indices for extraction. - for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { - operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, - {inst->GetSingleWordInOperand(i)}}); - } + analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + if (result_type->AsVector() == nullptr) { + uint32_t id = inst->GetSingleWordInOperand(result_index); + return {Operand(SPV_OPERAND_TYPE_ID, {id})}; + } - } else { - // With vectors we have to handle the case where it is concatenating - // vectors. - assert(inst->NumInOperands() == 2 && - "Expecting a vector of scalar values."); - - uint32_t element_index = inst->GetSingleWordInOperand(1); - for (uint32_t construct_index = 0; - construct_index < cinst->NumInOperands(); ++construct_index) { - uint32_t element_id = cinst->GetSingleWordInOperand(construct_index); - Instruction* element_def = def_use_mgr->GetDef(element_id); - analysis::Vector* element_type = - type_mgr->GetType(element_def->type_id())->AsVector(); - if (element_type) { - uint32_t vector_size = element_type->element_count(); - if (vector_size <= element_index) { - // The element we want comes after this vector. - element_index -= vector_size; - } else { - // We want an element of this vector. - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); - operands.push_back( - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_index}}); - break; - } - } else { - if (element_index == 0) { - // This is a scalar, and we this is the element we are extracting. - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); - break; - } else { - // Skip over this scalar value. - --element_index; - } - } + // If the result type is a vector, then vector operands are concatenated. + uint32_t total_element_count = 0; + for (uint32_t idx = 0; idx < inst->NumInOperands(); ++idx) { + uint32_t element_count = + GetNumOfElementsContributedByOperand(context, inst, idx); + total_element_count += element_count; + if (result_index < total_element_count) { + std::vector operands; + uint32_t id = inst->GetSingleWordInOperand(idx); + Instruction* operand_def = def_use_mgr->GetDef(id); + analysis::Type* operand_type = type_mgr->GetType(operand_def->type_id()); + + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + if (operand_type->AsVector()) { + uint32_t start_index_of_id = total_element_count - element_count; + uint32_t index_into_id = result_index - start_index_of_id; + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index_into_id}}); } + return operands; } + } + return {}; +} + +bool CompositeConstructFeedingExtract( + IRContext* context, Instruction* inst, + const std::vector&) { + // If the input to an OpCompositeExtract is an OpCompositeConstruct, + // then we can simply use the appropriate element in the construction. + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + // If there are no index operands, then this rule cannot do anything. + if (inst->NumInOperands() <= 1) { + return false; + } + + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != SpvOpCompositeConstruct) { + return false; + } + uint32_t index_into_result = inst->GetSingleWordInOperand(1); + std::vector operands = + GetExtractOperandsForElementOfCompositeConstruct(context, cinst, + index_into_result); + + if (operands.empty()) { + return false; + } + + // Add the remaining indices for extraction. + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {inst->GetSingleWordInOperand(i)}}); + } + + if (operands.size() == 1) { // If there were no extra indices, then we have the final object. No need - // to extract even more. - if (operands.size() == 1) { - inst->SetOpcode(SpvOpCopyObject); - } + // to extract any more. + inst->SetOpcode(SpvOpCopyObject); + } - inst->SetInOperands(std::move(operands)); - return true; - }; + inst->SetInOperands(std::move(operands)); + return true; } // If the OpCompositeConstruct is simply putting back together elements that @@ -2505,7 +2537,7 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct); rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract()); - rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract()); + rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract); rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract()); rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract()); diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 292a869e..95241e12 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -3608,7 +3608,19 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingT "%4 = OpCompositeExtract %int %3 2\n" + "OpReturn\n" + "OpFunctionEnd", - 4, INT_0_ID) + 4, INT_0_ID), + // Test case 15: + // Don't fold extract fed by construct with vector result if the index is + // past the last element. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeConstruct %v2int %int_0 %int_0\n" + + "%3 = OpCompositeConstruct %v4int %2 %100 %int_0\n" + + "%4 = OpCompositeExtract %int %3 4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, 0) )); INSTANTIATE_TEST_SUITE_P(CompositeConstructFoldingTest, GeneralInstructionFoldingTest, -- cgit v1.2.3 From 0f4508752fd932e624f690dcedee06ed6fb1157c Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 21 Sep 2021 16:07:30 +0100 Subject: Fix UBSan error in spirv-dis fuzzer (#4517) Pending a more general solution for constructing a target environment based on the bytes of a test input, this change avoids a UBSan error caused by the existing approach. Fixes https://crbug.com/38087 --- test/fuzzers/spvtools_dis_fuzzer.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/fuzzers/spvtools_dis_fuzzer.cpp b/test/fuzzers/spvtools_dis_fuzzer.cpp index ca9a52d8..5a727221 100644 --- a/test/fuzzers/spvtools_dis_fuzzer.cpp +++ b/test/fuzzers/spvtools_dis_fuzzer.cpp @@ -22,13 +22,24 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < sizeof(spv_target_env) + 1) return 0; - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4450): A more + // general solution to choosing the target environment based on the input + // buffer should ultimately be used. + uint32_t first_data_word = *reinterpret_cast(data); + spv_target_env target_env = static_cast( + first_data_word % (static_cast(SPV_ENV_VULKAN_1_2) + 1)); + const spv_context context = spvContextCreate(target_env); if (context == nullptr) return 0; data += sizeof(spv_target_env); size -= sizeof(spv_target_env); + if (size < 4) { + // There are not enough bytes to constitute a binary that can be + // disassembled. + return 0; + } + std::vector input; input.resize(size >> 2); size_t count = 0; -- cgit v1.2.3 From a6c5056db218459d02aadbcb43821322f50430c8 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Tue, 21 Sep 2021 11:15:16 -0400 Subject: Fix checks for offset in nested structs (#4531) Fixes #4533 Fixes https://crbug.com/38771 * Fixes offset checks to look through arrays for nested structures --- source/val/validate_decorations.cpp | 28 +++++++++++++++++++--------- test/val/val_decoration_test.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index c483635b..8e4c42e4 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -129,18 +129,28 @@ std::vector getStructMembers(uint32_t struct_id, SpvOp type, // Returns whether the given structure is missing Offset decoration for any // member. Handles also nested structures. bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) { - std::vector hasOffset(getStructMembers(struct_id, vstate).size(), - false); - // Check offsets of member decorations - for (auto& decoration : vstate.id_decorations(struct_id)) { - if (SpvDecorationOffset == decoration.dec_type() && - Decoration::kInvalidMember != decoration.struct_member_index()) { - hasOffset[decoration.struct_member_index()] = true; + const auto* inst = vstate.FindDef(struct_id); + std::vector hasOffset; + std::vector struct_members; + if (inst->opcode() == SpvOpTypeStruct) { + // Check offsets of member decorations. + struct_members = getStructMembers(struct_id, vstate); + hasOffset.resize(struct_members.size(), false); + + for (auto& decoration : vstate.id_decorations(struct_id)) { + if (SpvDecorationOffset == decoration.dec_type() && + Decoration::kInvalidMember != decoration.struct_member_index()) { + hasOffset[decoration.struct_member_index()] = true; + } } + } else if (inst->opcode() == SpvOpTypeArray || + inst->opcode() == SpvOpTypeRuntimeArray) { + hasOffset.resize(1, true); + struct_members.push_back(inst->GetOperandAs(1u)); } - // Check also nested structures + // Look through nested structs (which may be in an array). bool nestedStructsMissingOffset = false; - for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) { + for (auto id : struct_members) { if (isMissingOffsetInStruct(id, vstate)) { nestedStructsMissingOffset = true; break; diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index b9a413e3..9fdeb38f 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -7791,6 +7791,40 @@ OpFunctionEnd "member 0 is a matrix with stride 3 not satisfying alignment to 4")); } +TEST_F(ValidateDecorations, MissingOffsetStructNestedInArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %array ArrayStride 4 +OpDecorate %outer Block +OpMemberDecorate %outer 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%inner = OpTypeStruct %int +%array = OpTypeArray %inner %int_4 +%outer = OpTypeStruct %array +%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer +%var = OpVariable %ptr_ssbo_outer StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 3 decorated as Block must be explicitly " + "laid out with Offset decorations")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 1d48824bae5a88142a1bb5fb1ff603c8d0a31146 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 22 Sep 2021 15:52:19 -0400 Subject: Update SPIRV-Headers (#4543) * Remove tests that expected fast math mode operands to require a capability --- DEPS | 2 +- test/operand_capabilities_test.cpp | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/DEPS b/DEPS index ef37c6c2..5212deae 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', 'googletest_revision': '955c7f837efad184ec63e771c42542d37545eaef', 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', - 'spirv_headers_revision': '0d3c45cdbb4563b95be9037ea967aac815caf78f', + 'spirv_headers_revision': 'ae217c17809fadb232ec94b29304b4afcd417bb4', } deps = { diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp index 6f83dfeb..bc0ee055 100644 --- a/test/operand_capabilities_test.cpp +++ b/test/operand_capabilities_test.cpp @@ -368,19 +368,6 @@ INSTANTIATE_TEST_SUITE_P( // clang-format on }))); -// See SPIR-V Section 3.15 FP Fast Math Mode -INSTANTIATE_TEST_SUITE_P( - FPFastMathMode, EnumCapabilityTest, - Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), - ValuesIn(std::vector{ - CASE0(FP_FAST_MATH_MODE, FPFastMathModeMaskNone), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotNaNMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotInfMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNSZMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeAllowRecipMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeFastMask, Kernel), - }))); - // See SPIR-V Section 3.17 Linkage Type INSTANTIATE_TEST_SUITE_P( LinkageType, EnumCapabilityTest, -- cgit v1.2.3 From 134d763f4593f823b5438cb856eb905904fa5756 Mon Sep 17 00:00:00 2001 From: Jamie Madill Date: Thu, 23 Sep 2021 10:18:06 -0400 Subject: GN: Fix build for debuginfo codegen. (#4536) Fixes #4535 --- BUILD.gn | 49 +++++++------------------------------------------ 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index a1da931e..4bf3f79e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -225,29 +225,6 @@ template("spvtools_vendor_table") { } } -template("spvtools_vendor_table_local") { - assert(defined(invoker.name), "Need name in $target_name generation.") - - action("spvtools_vendor_tables_" + target_name) { - script = "utils/generate_grammar_tables.py" - - name = invoker.name - extinst_vendor_grammar = "source/extinst.${name}.grammar.json" - extinst_file = "${target_gen_dir}/${name}.insts.inc" - - args = [ - "--extinst-vendor-grammar", - rebase_path(extinst_vendor_grammar, root_build_dir), - "--vendor-insts-output", - rebase_path(extinst_file, root_build_dir), - "--vendor-operand-kind-prefix", - invoker.operand_kind_prefix, - ] - inputs = [ extinst_vendor_grammar ] - outputs = [ extinst_file ] - } -} - action("spvtools_generators_inc") { script = "utils/generate_registry_tables.py" @@ -333,13 +310,12 @@ spvtools_vendor_tables = [ "nonsemantic.clspvreflection", "...nil...", ], + [ + "nonsemantic.shader.debuginfo.100", + "SHDEBUG100_", + ], ] -spvtools_vendor_tables_local = [ [ - "nonsemantic.shader.debuginfo.100", - "SHDEBUG100_", - ] ] - foreach(table_def, spvtools_vendor_tables) { spvtools_vendor_table(table_def[0]) { name = table_def[0] @@ -347,13 +323,6 @@ foreach(table_def, spvtools_vendor_tables) { } } -foreach(table_def, spvtools_vendor_tables_local) { - spvtools_vendor_table_local(table_def[0]) { - name = table_def[0] - operand_kind_prefix = table_def[1] - } -} - config("spvtools_public_config") { include_dirs = [ "include" ] } @@ -421,10 +390,6 @@ static_library("spvtools") { target_name = table_def[0] deps += [ ":spvtools_vendor_tables_$target_name" ] } - foreach(table_def, spvtools_vendor_tables_local) { - target_name = table_def[0] - deps += [ ":spvtools_vendor_tables_$target_name" ] - } sources = [ "source/assembly_grammar.cpp", @@ -610,14 +575,14 @@ static_library("spvtools_opt") { "source/opt/constants.h", "source/opt/control_dependence.cpp", "source/opt/control_dependence.h", - "source/opt/convert_to_sampled_image_pass.cpp", - "source/opt/convert_to_sampled_image_pass.h", "source/opt/convert_to_half_pass.cpp", "source/opt/convert_to_half_pass.h", + "source/opt/convert_to_sampled_image_pass.cpp", + "source/opt/convert_to_sampled_image_pass.h", "source/opt/copy_prop_arrays.cpp", "source/opt/copy_prop_arrays.h", - "source/opt/dataflow.h", "source/opt/dataflow.cpp", + "source/opt/dataflow.h", "source/opt/dead_branch_elim_pass.cpp", "source/opt/dead_branch_elim_pass.h", "source/opt/dead_insert_elim_pass.cpp", -- cgit v1.2.3 From f125452cf8fc3d359a388402812cda76ca167c98 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Thu, 23 Sep 2021 10:59:38 -0600 Subject: Fix inst_buff_addr_check to handle struct loads (#4489) --- source/opt/inst_buff_addr_check_pass.cpp | 60 ++++++++- source/opt/inst_buff_addr_check_pass.h | 12 +- test/opt/inst_buff_addr_check_test.cpp | 206 +++++++++++++++++++++++++++++++ 3 files changed, 273 insertions(+), 5 deletions(-) diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp index 06acc7ea..e2336d36 100644 --- a/source/opt/inst_buff_addr_check_pass.cpp +++ b/source/opt/inst_buff_addr_check_pass.cpp @@ -130,13 +130,48 @@ void InstBuffAddrCheckPass::GenCheckCode( context()->KillInst(ref_inst); } +uint32_t InstBuffAddrCheckPass::GetTypeAlignment(uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeVector: + return GetTypeLength(type_id); + case SpvOpTypeMatrix: + return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypeStruct: { + uint32_t max = 0; + type_inst->ForEachInId([&max, this](const uint32_t* iid) { + uint32_t alignment = GetTypeAlignment(*iid); + max = (alignment > max) ? alignment : max; + }); + return max; + } + case SpvOpTypePointer: + assert(type_inst->GetSingleWordInOperand(0) == + SpvStorageClassPhysicalStorageBufferEXT && + "unexpected pointer type"); + return 8u; + default: + assert(false && "unexpected type"); + return 0; + } +} + uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case SpvOpTypeFloat: case SpvOpTypeInt: return type_inst->GetSingleWordInOperand(0) / 8u; - case SpvOpTypeVector: + case SpvOpTypeVector: { + uint32_t raw_cnt = type_inst->GetSingleWordInOperand(1); + uint32_t adj_cnt = (raw_cnt == 3u) ? 4u : raw_cnt; + return adj_cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); + } case SpvOpTypeMatrix: return type_inst->GetSingleWordInOperand(1) * GetTypeLength(type_inst->GetSingleWordInOperand(0)); @@ -145,8 +180,29 @@ uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { SpvStorageClassPhysicalStorageBufferEXT && "unexpected pointer type"); return 8u; + case SpvOpTypeArray: { + uint32_t const_id = type_inst->GetSingleWordInOperand(1); + Instruction* const_inst = get_def_use_mgr()->GetDef(const_id); + uint32_t cnt = const_inst->GetSingleWordInOperand(0); + return cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); + } + case SpvOpTypeStruct: { + uint32_t len = 0; + type_inst->ForEachInId([&len, this](const uint32_t* iid) { + // Align struct length + uint32_t alignment = GetTypeAlignment(*iid); + uint32_t mod = len % alignment; + uint32_t diff = (mod != 0) ? alignment - mod : 0; + len += diff; + // Increment struct length by component length + uint32_t comp_len = GetTypeLength(*iid); + len += comp_len; + }); + return len; + } + case SpvOpTypeRuntimeArray: default: - assert(false && "unexpected buffer reference type"); + assert(false && "unexpected type"); return 0; } } diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h index ec7bb684..a8232239 100644 --- a/source/opt/inst_buff_addr_check_pass.h +++ b/source/opt/inst_buff_addr_check_pass.h @@ -28,7 +28,9 @@ namespace opt { // external design of this class may change as the layer evolves. class InstBuffAddrCheckPass : public InstrumentPass { public: - // Preferred interface + // For test harness only + InstBuffAddrCheckPass() : InstrumentPass(7, 23, kInstValidationIdBuffAddr) {} + // For all other interfaces InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id) : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {} @@ -40,8 +42,12 @@ class InstBuffAddrCheckPass : public InstrumentPass { const char* name() const override { return "inst-bindless-check-pass"; } private: - // Return byte length of type |type_id|. Must be int, float, vector, matrix - // or physical pointer. + // Return byte alignment of type |type_id|. Must be int, float, vector, + // matrix, struct, array or physical pointer. Uses std430 alignment. + uint32_t GetTypeAlignment(uint32_t type_id); + + // Return byte length of type |type_id|. Must be int, float, vector, matrix, + // struct, array or physical pointer. Uses std430 alignment and sizes. uint32_t GetTypeLength(uint32_t type_id); // Add |type_id| param to |input_func| and add id to |param_vec|. diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp index 41ead67a..95114b23 100644 --- a/test/opt/inst_buff_addr_check_test.cpp +++ b/test/opt/inst_buff_addr_check_test.cpp @@ -615,6 +615,212 @@ OpFunctionEnd true, 7u, 23u); } +TEST_F(InstBuffAddrTest, StructLoad) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // #extension GL_ARB_gpu_shader_int64 : enable + // struct Test { + // float a; + // }; + // + // layout(buffer_reference, std430, buffer_reference_align = 16) buffer + // TestBuffer { Test test; }; + // + // Test GetTest(uint64_t ptr) { + // return TestBuffer(ptr).test; + // } + // + // void main() { + // GetTest(0xe0000000); + // } + + const std::string defs = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +; CHECK: OpEntryPoint Fragment %main "main" %60 %99 %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_ARB_gpu_shader_int64" +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %Test "Test" +OpMemberName %Test 0 "a" +OpName %Test_0 "Test" +OpMemberName %Test_0 0 "a" +OpName %TestBuffer "TestBuffer" +OpMemberName %TestBuffer 0 "test" +)"; + + const std::string decorates = + R"( +OpMemberDecorate %Test_0 0 Offset 0 +OpMemberDecorate %TestBuffer 0 Offset 0 +OpDecorate %TestBuffer Block +; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 +; CHECK: OpDecorate %_struct_58 Block +; CHECK: OpMemberDecorate %_struct_58 0 Offset 0 +; CHECK: OpDecorate %60 DescriptorSet 7 +; CHECK: OpDecorate %60 Binding 2 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +; CHECK: OpDecorate %_struct_97 Block +; CHECK: OpMemberDecorate %_struct_97 0 Offset 0 +; CHECK: OpMemberDecorate %_struct_97 1 Offset 4 +; CHECK: OpDecorate %99 DescriptorSet 7 +; CHECK: OpDecorate %99 Binding 0 +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)"; + + const std::string globals = + R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%float = OpTypeFloat 32 +%Test = OpTypeStruct %float +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer +%Test_0 = OpTypeStruct %float +%TestBuffer = OpTypeStruct %Test_0 +%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0 +%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704 +; CHECK: %47 = OpTypeFunction %bool %ulong %uint +; CHECK: %_struct_58 = OpTypeStruct %_runtimearr_ulong +; CHECK: %60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer +; CHECK: %90 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_struct_97 = OpTypeStruct %uint %_runtimearr_uint +; CHECK: %99 = OpVariable %_ptr_StorageBuffer__struct_97 StorageBuffer +; CHECK: %143 = OpConstantNull %Test_0 +)"; + + const std::string main = + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +%37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704 +%38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0 +%39 = OpLoad %Test_0 %38 Aligned 16 +; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16 +; CHECK: %43 = OpConvertPtrToU %ulong %38 +; CHECK: %80 = OpFunctionCall %bool %45 %43 %uint_4 +; CHECK: OpSelectionMerge %81 None +; CHECK: OpBranchConditional %80 %82 %83 +; CHECK: %82 = OpLabel +; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16 +; CHECK: OpBranch %81 +; CHECK: %83 = OpLabel +; CHECK: %85 = OpUConvert %uint %43 +; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32 +; CHECK: %88 = OpUConvert %uint %87 +; CHECK: %142 = OpFunctionCall %void %89 %uint_37 %uint_2 %85 %88 +; CHECK: OpBranch %81 +; CHECK: %81 = OpLabel +; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83 +%40 = OpCopyLogical %Test %39 +; CHECK-NOT: %40 = OpCopyLogical %Test %39 +; CHECK: %40 = OpCopyLogical %Test %144 +OpReturn +OpFunctionEnd +)"; + + const std::string output_funcs = + R"( +; CHECK: %45 = OpFunction %bool None %47 +; CHECK: %48 = OpFunctionParameter %ulong +; CHECK: %49 = OpFunctionParameter %uint +; CHECK: %50 = OpLabel +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %53 = OpPhi %uint %uint_1 %50 %54 %52 +; CHECK: OpLoopMerge %56 %52 None +; CHECK: OpBranch %52 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpIAdd %uint %53 %uint_1 +; CHECK: %63 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %54 +; CHECK: %64 = OpLoad %ulong %63 +; CHECK: %65 = OpUGreaterThan %bool %64 %48 +; CHECK: OpBranchConditional %65 %56 %51 +; CHECK: %56 = OpLabel +; CHECK: %66 = OpISub %uint %54 %uint_1 +; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %66 +; CHECK: %68 = OpLoad %ulong %67 +; CHECK: %69 = OpISub %ulong %48 %68 +; CHECK: %70 = OpUConvert %ulong %49 +; CHECK: %71 = OpIAdd %ulong %69 %70 +; CHECK: %72 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %uint_0 +; CHECK: %73 = OpLoad %ulong %72 +; CHECK: %74 = OpUConvert %uint %73 +; CHECK: %75 = OpISub %uint %66 %uint_1 +; CHECK: %76 = OpIAdd %uint %75 %74 +; CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %76 +; CHECK: %78 = OpLoad %ulong %77 +; CHECK: %79 = OpULessThanEqual %bool %71 %78 +; CHECK: OpReturnValue %79 +; CHECK: OpFunctionEnd +; CHECK: %89 = OpFunction %void None %90 +; CHECK: %91 = OpFunctionParameter %uint +; CHECK: %92 = OpFunctionParameter %uint +; CHECK: %93 = OpFunctionParameter %uint +; CHECK: %94 = OpFunctionParameter %uint +; CHECK: %95 = OpLabel +; CHECK: %101 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_0 +; CHECK: %103 = OpAtomicIAdd %uint %101 %uint_4 %uint_0 %uint_10 +; CHECK: %104 = OpIAdd %uint %103 %uint_10 +; CHECK: %105 = OpArrayLength %uint %99 1 +; CHECK: %106 = OpULessThanEqual %bool %104 %105 +; CHECK: OpSelectionMerge %107 None +; CHECK: OpBranchConditional %106 %108 %107 +; CHECK: %108 = OpLabel +; CHECK: %109 = OpIAdd %uint %103 %uint_0 +; CHECK: %110 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %109 +; CHECK: OpStore %110 %uint_10 +; CHECK: %112 = OpIAdd %uint %103 %uint_1 +; CHECK: %113 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %112 +; CHECK: OpStore %113 %uint_23 +; CHECK: %114 = OpIAdd %uint %103 %uint_2 +; CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %114 +; CHECK: OpStore %115 %91 +; CHECK: %117 = OpIAdd %uint %103 %uint_3 +; CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %117 +; CHECK: OpStore %118 %uint_4 +; CHECK: %122 = OpLoad %v4float %gl_FragCoord +; CHECK: %124 = OpBitcast %v4uint %122 +; CHECK: %125 = OpCompositeExtract %uint %124 0 +; CHECK: %126 = OpIAdd %uint %103 %uint_4 +; CHECK: %127 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %126 +; CHECK: OpStore %127 %125 +; CHECK: %128 = OpCompositeExtract %uint %124 1 +; CHECK: %130 = OpIAdd %uint %103 %uint_5 +; CHECK: %131 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %130 +; CHECK: OpStore %131 %128 +; CHECK: %133 = OpIAdd %uint %103 %uint_7 +; CHECK: %134 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %133 +; CHECK: OpStore %134 %92 +; CHECK: %136 = OpIAdd %uint %103 %uint_8 +; CHECK: %137 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %136 +; CHECK: OpStore %137 %93 +; CHECK: %139 = OpIAdd %uint %103 %uint_9 +; CHECK: %140 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %139 +; CHECK: OpStore %140 %94 +; CHECK: OpBranch %107 +; CHECK: %107 = OpLabel +; CHECK: OpReturn +; CHECK: OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch( + defs + decorates + globals + main + output_funcs, true); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From 19dc86c48c7bf10181cfbbdad2c73bc55da13864 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Fri, 24 Sep 2021 08:56:08 -0600 Subject: Handle NonSemantic.Shader Debug[No]Line (#4530) Debug[No]Line are tracked and optimized using the same mechanism that tracks and optimizes Op[No]Line. Also: - Fix missing DebugScope at top of block. - Allow scalar replacement of access chain in DebugDeclare --- source/opt/aggressive_dead_code_elim_pass.cpp | 8 ++ source/opt/block_merge_util.cpp | 4 +- source/opt/compact_ids_pass.cpp | 5 +- source/opt/debug_info_manager.cpp | 4 +- source/opt/def_use_manager.cpp | 21 ++- source/opt/feature_manager.cpp | 6 +- source/opt/feature_manager.h | 6 +- source/opt/if_conversion.cpp | 1 + source/opt/inline_pass.cpp | 4 +- source/opt/instruction.cpp | 67 ++++++++-- source/opt/instruction.h | 24 ++-- source/opt/ir_context.cpp | 8 +- source/opt/ir_context.h | 4 + source/opt/ir_loader.cpp | 29 ++++- source/opt/loop_unroller.cpp | 6 +- source/opt/module.cpp | 37 ++++-- source/opt/pass.cpp | 4 +- source/opt/reflect.h | 2 +- source/opt/replace_invalid_opc.cpp | 18 ++- source/opt/scalar_replacement_pass.cpp | 12 ++ source/opt/scalar_replacement_pass.h | 3 + source/val/validate_layout.cpp | 4 + test/opt/aggressive_dead_code_elim_test.cpp | 21 +++ test/opt/loop_optimizations/unroll_simple.cpp | 179 ++++++++++++++++++++++++++ 24 files changed, 413 insertions(+), 64 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 1173eafe..cb000087 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -441,6 +441,14 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { // Perform closure on live instruction set. while (!worklist_.empty()) { Instruction* liveInst = worklist_.front(); + // Add all operand instructions of Debug[No]Lines + for (auto& lineInst : liveInst->dbg_line_insts()) { + if (lineInst.IsDebugLineInst()) { + lineInst.ForEachInId([this](const uint32_t* iid) { + AddToWorklist(get_def_use_mgr()->GetDef(*iid)); + }); + } + } // Add all operand instructions if not already live liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) { Instruction* inInst = get_def_use_mgr()->GetDef(*iid); diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp index 15e8c6ff..e4ba10fc 100644 --- a/source/opt/block_merge_util.cpp +++ b/source/opt/block_merge_util.cpp @@ -173,7 +173,9 @@ void MergeWithSuccessor(IRContext* context, Function* func, auto& vec = terminator->dbg_line_insts(); auto& new_vec = merge_inst->dbg_line_insts(); new_vec.insert(new_vec.end(), vec.begin(), vec.end()); - terminator->clear_dbg_line_insts(); + terminator->ClearDbgLineInsts(); + for (auto& l_inst : new_vec) + context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); // Move the merge instruction to just before the terminator. merge_inst->InsertBefore(terminator); diff --git a/source/opt/compact_ids_pass.cpp b/source/opt/compact_ids_pass.cpp index 67091531..8815b8c6 100644 --- a/source/opt/compact_ids_pass.cpp +++ b/source/opt/compact_ids_pass.cpp @@ -86,9 +86,12 @@ Pass::Status CompactIdsPass::Process() { }, true); - if (modified) + if (modified) { context()->module()->SetIdBound( static_cast(result_id_mapping.size() + 1)); + // There are ids in the feature manager that could now be invalid + context()->ResetFeatureManager(); + } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index 357e1ecc..6593c3fb 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -86,7 +86,7 @@ uint32_t DebugInfoManager::GetDbgSetImportId() { context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); if (setId == 0) { setId = - context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo(); + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); } return setId; } @@ -158,7 +158,7 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant, // not literals. if (setId == - context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID; uint32_t line_number = 0; diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp index 0ec98cae..394b9fa1 100644 --- a/source/opt/def_use_manager.cpp +++ b/source/opt/def_use_manager.cpp @@ -58,7 +58,7 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { case SPV_OPERAND_TYPE_SCOPE_ID: { uint32_t use_id = inst->GetSingleWordOperand(i); Instruction* def = GetDef(use_id); - assert(def && "Definition is not registered."); + if (!def) assert(false && "Definition is not registered."); id_to_users_.insert(UserEntry(def, inst)); used_ids->push_back(use_id); } break; @@ -71,6 +71,9 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { void DefUseManager::AnalyzeInstDefUse(Instruction* inst) { AnalyzeInstDef(inst); AnalyzeInstUse(inst); + // Analyze lines last otherwise they will be cleared when inst is + // cleared by preceding two calls + for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst); } void DefUseManager::UpdateDefUse(Instruction* inst) { @@ -224,9 +227,11 @@ void DefUseManager::AnalyzeDefUse(Module* module) { if (!module) return; // Analyze all the defs before any uses to catch forward references. module->ForEachInst( - std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1)); + std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1), + true); module->ForEachInst( - std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1)); + std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1), + true); } void DefUseManager::ClearInst(Instruction* inst) { @@ -261,6 +266,16 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) { if (lhs.id_to_def_ != rhs.id_to_def_) { + for (auto p : lhs.id_to_def_) { + if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) { + return false; + } + } + for (auto p : rhs.id_to_def_) { + if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) { + return false; + } + } return false; } diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index e00eaac9..39a4a348 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -80,7 +80,7 @@ void FeatureManager::AddExtInstImportIds(Module* module) { extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); extinst_importid_OpenCL100DebugInfo_ = module->GetExtInstImportId("OpenCL.DebugInfo.100"); - extinst_importid_Vulkan100DebugInfo_ = + extinst_importid_Shader100DebugInfo_ = module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100"); } @@ -109,8 +109,8 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) { return false; } - if (a.extinst_importid_Vulkan100DebugInfo_ != - b.extinst_importid_Vulkan100DebugInfo_) { + if (a.extinst_importid_Shader100DebugInfo_ != + b.extinst_importid_Shader100DebugInfo_) { return false; } diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index f66193fd..68c8e9a2 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h @@ -55,8 +55,8 @@ class FeatureManager { return extinst_importid_OpenCL100DebugInfo_; } - uint32_t GetExtInstImportId_Vulkan100DebugInfo() const { - return extinst_importid_Vulkan100DebugInfo_; + uint32_t GetExtInstImportId_Shader100DebugInfo() const { + return extinst_importid_Shader100DebugInfo_; } friend bool operator==(const FeatureManager& a, const FeatureManager& b); @@ -99,7 +99,7 @@ class FeatureManager { // Common NonSemanticShader100DebugInfo external instruction import ids, // cached for performance. - uint32_t extinst_importid_Vulkan100DebugInfo_ = 0; + uint32_t extinst_importid_Shader100DebugInfo_ = 0; }; } // namespace opt diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp index 4284069a..49206617 100644 --- a/source/opt/if_conversion.cpp +++ b/source/opt/if_conversion.cpp @@ -129,6 +129,7 @@ Pass::Status IfConversion::Process() { Instruction* select = builder.AddSelect(phi->type_id(), condition, true_value->result_id(), false_value->result_id()); + context()->get_def_use_mgr()->AnalyzeInstDefUse(select); select->UpdateDebugInfoFrom(phi); context()->ReplaceAllUsesWith(phi->result_id(), select->result_id()); to_kill.push_back(phi); diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index 8b91ad6c..2cc31258 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp @@ -92,7 +92,7 @@ void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}})); if (line_inst != nullptr) { - newStore->dbg_line_insts().push_back(*line_inst); + newStore->AddDebugLine(line_inst); } newStore->SetDebugScope(dbg_scope); (*block_ptr)->AddInstruction(std::move(newStore)); @@ -106,7 +106,7 @@ void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id, new Instruction(context(), SpvOpLoad, type_id, resultId, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}})); if (line_inst != nullptr) { - newLoad->dbg_line_insts().push_back(*line_inst); + newLoad->AddDebugLine(line_inst); } newLoad->SetDebugScope(dbg_scope); (*block_ptr)->AddInstruction(std::move(newLoad)); diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 174b74f3..665b72f0 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -73,8 +73,6 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, unique_id_(c->TakeNextUniqueId()), 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) { const auto& current_payload = inst.operands[i]; std::vector words( @@ -82,6 +80,8 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, inst.words + current_payload.offset + current_payload.num_words); operands_.emplace_back(current_payload.type, std::move(words)); } + assert((!IsLineInst() || dbg_line.empty()) && + "Op(No)Line attaching to Op(No)Line found"); } Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, @@ -158,6 +158,10 @@ Instruction* Instruction::Clone(IRContext* c) const { clone->unique_id_ = c->TakeNextUniqueId(); clone->operands_ = operands_; clone->dbg_line_insts_ = dbg_line_insts_; + for (auto& i : clone->dbg_line_insts_) { + i.unique_id_ = c->TakeNextUniqueId(); + if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId()); + } clone->dbg_scope_ = dbg_scope_; return clone; } @@ -511,7 +515,7 @@ void Instruction::UpdateLexicalScope(uint32_t scope) { for (auto& i : dbg_line_insts_) { i.dbg_scope_.SetLexicalScope(scope); } - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } @@ -522,24 +526,61 @@ void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) { for (auto& i : dbg_line_insts_) { i.dbg_scope_.SetInlinedAt(new_inlined_at); } - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } } +void Instruction::ClearDbgLineInsts() { + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + auto def_use_mgr = context()->get_def_use_mgr(); + for (auto& l_inst : dbg_line_insts_) def_use_mgr->ClearInst(&l_inst); + } + clear_dbg_line_insts(); +} + void Instruction::UpdateDebugInfoFrom(const Instruction* from) { if (from == nullptr) return; - clear_dbg_line_insts(); + ClearDbgLineInsts(); if (!from->dbg_line_insts().empty()) - dbg_line_insts().push_back(from->dbg_line_insts().back()); + AddDebugLine(&from->dbg_line_insts().back()); SetDebugScope(from->GetDebugScope()); - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } } +void Instruction::AddDebugLine(const Instruction* inst) { + dbg_line_insts_.push_back(*inst); + dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId(); + if (inst->IsDebugLineInst()) + dbg_line_insts_.back().SetResultId(context_->TakeNextId()); + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back()); +} + +bool Instruction::IsDebugLineInst() const { + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) || + (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine)); +} + +bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); } + +bool Instruction::IsLine() const { + if (opcode() == SpvOpLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugLine; +} + +bool Instruction::IsNoLine() const { + if (opcode() == SpvOpNoLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine; +} + Instruction* Instruction::InsertBefore(std::unique_ptr&& inst) { inst.get()->InsertBefore(this); return inst.release(); @@ -629,12 +670,12 @@ NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode() return NonSemanticShaderDebugInfo100InstructionsMax; } - if (!context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) { + if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { return NonSemanticShaderDebugInfo100InstructionsMax; } if (GetSingleWordInOperand(kExtInstSetIdInIdx) != - context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) { + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { return NonSemanticShaderDebugInfo100InstructionsMax; } @@ -649,16 +690,16 @@ CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const { const uint32_t opencl_set_id = context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); - const uint32_t vulkan_set_id = - context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo(); + const uint32_t shader_set_id = + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); - if (!opencl_set_id && !vulkan_set_id) { + if (!opencl_set_id && !shader_set_id) { return CommonDebugInfoInstructionsMax; } const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx); - if (used_set_id != opencl_set_id && used_set_id != vulkan_set_id) { + if (used_set_id != opencl_set_id && used_set_id != shader_set_id) { return CommonDebugInfoInstructionsMax; } diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 5019170e..c962e509 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -252,11 +252,6 @@ class Instruction : public utils::IntrusiveNodeBase { // Clear line-related debug instructions attached to this instruction. void clear_dbg_line_insts() { dbg_line_insts_.clear(); } - // Set line-related debug instructions. - void set_dbg_line_insts(const std::vector& lines) { - dbg_line_insts_ = lines; - } - // Same semantics as in the base class except the list the InstructionList // containing |pos| will now assume ownership of |this|. // inline void MoveBefore(Instruction* pos); @@ -306,8 +301,21 @@ class Instruction : public utils::IntrusiveNodeBase { // Sets DebugScope. inline void SetDebugScope(const DebugScope& scope); inline const DebugScope& GetDebugScope() const { return dbg_scope_; } + // Add debug line inst. Renew result id if Debug[No]Line + void AddDebugLine(const Instruction* inst); // Updates DebugInlinedAt of DebugScope and OpLine. void UpdateDebugInlinedAt(uint32_t new_inlined_at); + // Clear line-related debug instructions attached to this instruction + // along with def-use entries. + void ClearDbgLineInsts(); + // Return true if Shader100:Debug[No]Line + bool IsDebugLineInst() const; + // Return true if Op[No]Line or Shader100:Debug[No]Line + bool IsLineInst() const; + // Return true if OpLine or Shader100:DebugLine + bool IsLine() const; + // Return true if OpNoLine or Shader100:DebugNoLine + bool IsNoLine() const; inline uint32_t GetDebugInlinedAt() const { return dbg_scope_.GetInlinedAt(); } @@ -609,9 +617,9 @@ class Instruction : public utils::IntrusiveNodeBase { uint32_t unique_id_; // Unique instruction id // All logical operands, including result type id and result id. OperandList operands_; - // Opline and OpNoLine instructions preceding this instruction. Note that for - // Instructions representing OpLine or OpNonLine itself, this field should be - // empty. + // Op[No]Line or Debug[No]Line instructions preceding this instruction. Note + // that for Instructions representing Op[No]Line or Debug[No]Line themselves, + // this field should be empty. std::vector dbg_line_insts_; // DebugScope that wraps this instruction. diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 2b8fe1fb..612a831a 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -171,7 +171,9 @@ Instruction* IRContext::KillInst(Instruction* inst) { KillOperandFromDebugInstructions(inst); if (AreAnalysesValid(kAnalysisDefUse)) { - get_def_use_mgr()->ClearInst(inst); + analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); + def_use_mgr->ClearInst(inst); + for (auto& l_inst : inst->dbg_line_insts()) def_use_mgr->ClearInst(&l_inst); } if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { instr_to_block_.erase(inst); @@ -218,6 +220,8 @@ Instruction* IRContext::KillInst(Instruction* inst) { void IRContext::CollectNonSemanticTree( Instruction* inst, std::unordered_set* to_kill) { if (!inst->HasResultId()) return; + // Debug[No]Line result id is not used, so we are done + if (inst->IsDebugLineInst()) return; std::vector work_list; std::unordered_set seen; work_list.push_back(inst); @@ -930,7 +934,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { while (line_inst != nullptr) { // Stop at the beginning of the basic block. if (!line_inst->dbg_line_insts().empty()) { line_inst = &line_inst->dbg_line_insts().back(); - if (line_inst->opcode() == SpvOpNoLine) { + if (line_inst->IsNoLine()) { line_inst = nullptr; } break; diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 3b89d85c..65853476 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -98,6 +98,7 @@ class IRContext { module_(new Module()), consumer_(std::move(c)), def_use_mgr_(nullptr), + feature_mgr_(nullptr), valid_analyses_(kAnalysisNone), constant_mgr_(nullptr), type_mgr_(nullptr), @@ -116,6 +117,7 @@ class IRContext { module_(std::move(m)), consumer_(std::move(c)), def_use_mgr_(nullptr), + feature_mgr_(nullptr), valid_analyses_(kAnalysisNone), type_mgr_(nullptr), id_to_name_(nullptr), @@ -769,6 +771,8 @@ class IRContext { // The instruction decoration manager for |module_|. std::unique_ptr decoration_mgr_; + + // The feature manager for |module_|. std::unique_ptr feature_mgr_; // A map from instructions to the basic block they belong to. This mapping is diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index 87718036..a82b530e 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -19,6 +19,7 @@ #include "DebugInfo.h" #include "OpenCLDebugInfo100.h" #include "source/ext_inst.h" +#include "source/opt/ir_context.h" #include "source/opt/log.h" #include "source/opt/reflect.h" #include "source/util/make_unique.h" @@ -37,20 +38,32 @@ IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) inst_index_(0), last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} +bool IsLineInst(const spv_parsed_instruction_t* inst) { + const auto opcode = static_cast(inst->opcode); + if (IsOpLineInst(opcode)) return true; + if (opcode != SpvOpExtInst) return false; + if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) + return false; + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine; +} + bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { ++inst_index_; - const auto opcode = static_cast(inst->opcode); - if (IsDebugLineInst(opcode)) { + if (IsLineInst(inst)) { module()->SetContainsDebugInfo(); last_line_inst_.reset(); - dbg_line_info_.push_back( - Instruction(module()->context(), *inst, last_dbg_scope_)); + dbg_line_info_.emplace_back(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. + const auto opcode = static_cast(inst->opcode); 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 || @@ -96,14 +109,20 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); if (!spv_inst->dbg_line_insts().empty()) { if (extra_line_tracking_ && - (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine)) { + (!spv_inst->dbg_line_insts().back().IsNoLine())) { last_line_inst_ = std::unique_ptr( spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); } dbg_line_info_.clear(); } else if (last_line_inst_ != nullptr) { last_line_inst_->SetDebugScope(last_dbg_scope_); spv_inst->dbg_line_insts().push_back(*last_line_inst_); + last_line_inst_ = std::unique_ptr( + spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); } const char* src = source_.c_str(); diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index df68bd20..bf622500 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp @@ -760,7 +760,7 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block, IRContext::Analysis::kAnalysisInstrToBlockMapping); Instruction* new_branch = builder.AddBranch(new_target); - new_branch->set_dbg_line_insts(lines); + if (!lines.empty()) new_branch->AddDebugLine(&lines.back()); new_branch->SetDebugScope(scope); } @@ -873,6 +873,10 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) { def_use_mgr->AnalyzeInstDefUse(basic_block->GetLabelInst()); for (Instruction& inst : *basic_block) { + // Do def/use analysis on new lines + for (auto& line : inst.dbg_line_insts()) + def_use_mgr->AnalyzeInstDefUse(&line); + uint32_t old_id = inst.result_id(); // Ignore stores etc. diff --git a/source/opt/module.cpp b/source/opt/module.cpp index 6585012f..f97defbd 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -151,16 +151,15 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { this](const Instruction* i) { // Skip emitting line instructions between merge and branch instructions. auto opcode = i->opcode(); - if (between_merge_and_branch && - (opcode == SpvOpLine || opcode == SpvOpNoLine)) { + if (between_merge_and_branch && i->IsLineInst()) { return; } between_merge_and_branch = false; if (last_line_inst != nullptr) { - // If the current instruction is OpLine and it is the same with - // the last line instruction that is still effective (can be applied + // If the current instruction is OpLine or DebugLine and it is the same + // as the last line instruction that is still effective (can be applied // to the next instruction), we skip writing the current instruction. - if (opcode == SpvOpLine) { + if (i->IsLine()) { uint32_t operand_index = 0; if (last_line_inst->WhileEachInOperand( [&operand_index, i](const uint32_t* word) { @@ -169,11 +168,22 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { })) { return; } - } else if (opcode != SpvOpNoLine && i->dbg_line_insts().empty()) { + } else if (!i->IsNoLine() && i->dbg_line_insts().empty()) { // If the current instruction does not have the line information, // the last line information is not effective any more. Emit OpNoLine - // to specify it. - binary->push_back((1 << 16) | static_cast(SpvOpNoLine)); + // or DebugNoLine to specify it. + uint32_t shader_set_id = context() + ->get_feature_mgr() + ->GetExtInstImportId_Shader100DebugInfo(); + if (shader_set_id != 0) { + binary->push_back((5 << 16) | static_cast(SpvOpExtInst)); + binary->push_back(context()->get_type_mgr()->GetVoidTypeId()); + binary->push_back(context()->TakeNextId()); + binary->push_back(shader_set_id); + binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine); + } else { + binary->push_back((1 << 16) | static_cast(SpvOpNoLine)); + } last_line_inst = nullptr; } } @@ -181,7 +191,7 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { if (opcode == SpvOpLabel) { between_label_and_phi_var = true; } else if (opcode != SpvOpVariable && opcode != SpvOpPhi && - opcode != SpvOpLine && opcode != SpvOpNoLine) { + !spvtools::opt::IsOpLineInst(opcode)) { between_label_and_phi_var = false; } @@ -190,7 +200,7 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { if (scope != last_scope) { // Can only emit nonsemantic instructions after all phi instructions // in a block so don't emit scope instructions before phi instructions - // for Vulkan.NonSemantic.DebugInfo.100. + // for NonSemantic.Shader.DebugInfo.100. if (!between_label_and_phi_var || context() ->get_feature_mgr() @@ -206,18 +216,19 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { i->ToBinaryWithoutAttachedDebugInsts(binary); } // Update the last line instruction. - if (spvOpcodeIsBlockTerminator(opcode) || opcode == SpvOpNoLine) { + if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) { last_line_inst = nullptr; } else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { between_merge_and_branch = true; last_line_inst = nullptr; - } else if (opcode == SpvOpLine) { + } else if (i->IsLine()) { last_line_inst = i; } }; ForEachInst(write_inst, true); - // We create new instructions for DebugScope. The bound must be updated. + // We create new instructions for DebugScope and DebugNoLine. The bound must + // be updated. binary->data()[bound_idx] = header_.bound; } diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp index 09b78af9..017aad10 100644 --- a/source/opt/pass.cpp +++ b/source/opt/pass.cpp @@ -43,8 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) { if (status == Status::SuccessWithChange) { ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses()); } - assert((status == Status::Failure || ctx->IsConsistent()) && - "An analysis in the context is out of date."); + if (!(status == Status::Failure || ctx->IsConsistent())) + assert(false && "An analysis in the context is out of date."); return status; } diff --git a/source/opt/reflect.h b/source/opt/reflect.h index c7d46df5..d300fe5d 100644 --- a/source/opt/reflect.h +++ b/source/opt/reflect.h @@ -34,7 +34,7 @@ inline bool IsDebug2Inst(SpvOp opcode) { inline bool IsDebug3Inst(SpvOp opcode) { return opcode == SpvOpModuleProcessed; } -inline bool IsDebugLineInst(SpvOp opcode) { +inline bool IsOpLineInst(SpvOp opcode) { return opcode == SpvOpLine || opcode == SpvOpNoLine; } inline bool IsAnnotationInst(SpvOp opcode) { diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp index 38b7539b..e3b9d3e4 100644 --- a/source/opt/replace_invalid_opc.cpp +++ b/source/opt/replace_invalid_opc.cpp @@ -71,10 +71,10 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, function->ForEachInst( [model, &modified, &last_line_dbg_inst, this](Instruction* inst) { // Track the debug information so we can have a meaningful message. - if (inst->opcode() == SpvOpLabel || inst->opcode() == SpvOpNoLine) { + if (inst->opcode() == SpvOpLabel || inst->IsNoLine()) { last_line_dbg_inst = nullptr; return; - } else if (inst->opcode() == SpvOpLine) { + } else if (inst->IsLine()) { last_line_dbg_inst = inst; return; } @@ -100,8 +100,18 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, ReplaceInstruction(inst, nullptr, 0, 0); } else { // Get the name of the source file. - Instruction* file_name = context()->get_def_use_mgr()->GetDef( - last_line_dbg_inst->GetSingleWordInOperand(0)); + uint32_t file_name_id = 0; + if (last_line_dbg_inst->opcode() == SpvOpLine) { + file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0); + } else { // Shader100::DebugLine + uint32_t debug_source_id = + last_line_dbg_inst->GetSingleWordInOperand(2); + Instruction* debug_source_inst = + context()->get_def_use_mgr()->GetDef(debug_source_id); + file_name_id = debug_source_inst->GetSingleWordInOperand(2); + } + Instruction* file_name = + context()->get_def_use_mgr()->GetDef(file_name_id); const char* source = reinterpret_cast( &file_name->GetInOperand(0).words[0]); diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index 388eb76d..8bc04991 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -27,6 +27,7 @@ static const uint32_t kDebugValueOperandValueIndex = 5; static const uint32_t kDebugValueOperandExpressionIndex = 6; +static const uint32_t kDebugDeclareOperandVariableIndex = 5; namespace spvtools { namespace opt { @@ -868,6 +869,11 @@ bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { case SpvOpImageTexelPointer: if (!CheckImageTexelPointer(index)) ok = false; break; + case SpvOpExtInst: + if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare || + !CheckDebugDeclare(index)) + ok = false; + break; default: ok = false; break; @@ -898,6 +904,12 @@ bool ScalarReplacementPass::CheckStore(const Instruction* inst, return false; return true; } + +bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const { + if (index != kDebugDeclareOperandVariableIndex) return false; + return true; +} + bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const { if (max_num_elements_ == 0) { return false; diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h index 9e9f0739..0928830c 100644 --- a/source/opt/scalar_replacement_pass.h +++ b/source/opt/scalar_replacement_pass.h @@ -142,6 +142,9 @@ class ScalarReplacementPass : public Pass { // of |inst| and the store is not to volatile memory. bool CheckStore(const Instruction* inst, uint32_t index) const; + // Returns true if the DebugDeclare can be scalarized at |index|. + bool CheckDebugDeclare(uint32_t index) const; + // Returns true if |index| is the pointer operand of an OpImageTexelPointer // instruction. bool CheckImageTexelPointer(uint32_t index) const; diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp index 90923fce..d5823219 100644 --- a/source/val/validate_layout.cpp +++ b/source/val/validate_layout.cpp @@ -58,6 +58,8 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || ext_inst_key == NonSemanticShaderDebugInfo100DebugFunctionDefinition) { local_debug_info = true; @@ -263,6 +265,8 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || ext_inst_key == NonSemanticShaderDebugInfo100DebugFunctionDefinition) { local_debug_info = true; diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index e49acf5b..0cff2681 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -7141,7 +7141,15 @@ TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) { %uint_16 = OpConstant %uint 16 %uint_17 = OpConstant %uint 17 %uint_19 = OpConstant %uint 19 + %uint_20 = OpConstant %uint 20 + %uint_21 = OpConstant %uint 21 + %uint_25 = OpConstant %uint 25 %uint_29 = OpConstant %uint 29 + %uint_30 = OpConstant %uint 30 + %uint_35 = OpConstant %uint 35 + %uint_41 = OpConstant %uint 41 + %uint_48 = OpConstant %uint 48 + %uint_53 = OpConstant %uint 53 %uint_64 = OpConstant %uint 64 %45 = OpTypeFunction %void %PS_INPUT = OpTypeStruct %v2float @@ -7198,20 +7206,33 @@ TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) { ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69 ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52 + %300 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30 %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 %89 = OpLoad %v2float %88 + %301 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 OpStore %79 %89 ;CHECK-NOT: OpStore %79 %89 + %302 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 %106 = OpExtInst %void %1 DebugValue %70 %89 %52 ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52 + %303 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_32 %91 = OpLoad %type_2d_image %g_tColor + %304 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_41 %uint_48 %92 = OpLoad %type_sampler %g_sAniso + %305 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_53 %94 = OpSampledImage %type_sampled_image %91 %92 %95 = OpImageSampleImplicitLod %v4float %94 %89 None + %306 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_5 %uint_53 %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0 OpStore %96 %95 + %307 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_12 %uint_20 %97 = OpLoad %PS_OUTPUT %78 + %308 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_5 %uint_20 OpStore %81 %97 + %309 = OpExtInst %void %1 DebugNoLine +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoLine %111 = OpExtInst %void %1 DebugNoScope ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope %100 = OpCompositeExtract %v4float %97 0 diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp index 6a3cb6ee..ac0dfde7 100644 --- a/test/opt/loop_optimizations/unroll_simple.cpp +++ b/test/opt/loop_optimizations/unroll_simple.cpp @@ -378,6 +378,185 @@ OpFunctionEnd)"; SinglePassRunAndMatch(text, true); } +TEST_F(PassClassTest, SimpleFullyUnrollWithShaderDebugInstructions) { + // We must preserve the debug information including + // NonSemantic.Shader.DebugInfo.100 instructions and DebugLine instructions. + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 330 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %2 "main" +OpName %5 "x" +OpName %3 "c" +OpDecorate %3 Location 0 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 4 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%uint_0 = OpConstant %14 0 +%uint_1 = OpConstant %14 1 +%uint_2 = OpConstant %14 2 +%uint_3 = OpConstant %14 3 +%uint_4 = OpConstant %14 4 +%uint_5 = OpConstant %14 5 +%uint_6 = OpConstant %14 6 +%uint_7 = OpConstant %14 7 +%uint_8 = OpConstant %14 8 +%uint_10 = OpConstant %14 10 +%uint_32 = OpConstant %14 32 +%15 = OpConstant %14 4 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpConstant %13 1 +%19 = OpTypePointer Function %13 +%20 = OpConstant %8 1 +%21 = OpTypeVector %13 4 +%22 = OpTypePointer Output %21 +%3 = OpVariable %22 Output +%null_expr = OpExtInst %6 %ext DebugExpression +%deref = OpExtInst %6 %ext DebugOperation %uint_0 +%deref_expr = OpExtInst %6 %ext DebugExpression %deref +%src = OpExtInst %6 %ext DebugSource %file_name +%cu = OpExtInst %6 %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5 +%dbg_tf = OpExtInst %6 %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0 +%dbg_v4f = OpExtInst %6 %ext DebugTypeVector %dbg_tf %uint_4 +%main_ty = OpExtInst %6 %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %6 %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10 +%bb = OpExtInst %6 %ext DebugLexicalBlock %src %uint_0 %uint_0 %dbg_main +%dbg_f = OpExtInst %6 %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%dbg_i = OpExtInst %6 %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_1 %uint_0 %bb %uint_4 + +; CHECK: [[f:%\w+]] = OpString "f" +; CHECK: [[i:%\w+]] = OpString "i" +; CHECK: [[int_0:%\w+]] = OpConstant {{%\w+}} 0 + +; CHECK: [[null_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression +; CHECK: [[deref:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugOperation %uint_0 +; CHECK: [[deref_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression [[deref]] +; CHECK: [[dbg_fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction +; CHECK: [[dbg_bb:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLexicalBlock +; CHECK: [[dbg_f:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[f]] {{%\w+}} {{%\w+}} %uint_0 %uint_0 [[dbg_fn]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[i]] {{%\w+}} {{%\w+}} %uint_1 %uint_0 [[dbg_bb]] + +%2 = OpFunction %6 None %7 +%23 = OpLabel + +; The first block has DebugDeclare and DebugValue with Deref +; +; CHECK: OpLabel +; CHECK: %x = OpVariable %_ptr_Function__arr_float_uint_4_0 Function +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugValue [[dbg_f]] [[int_0]] [[null_expr]] +; CHECK: OpBranch +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1 +; CHECK: OpSLessThan +; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0 +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_bb]] +; CHECK: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0 +; +; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0 + +; Other blocks do not have DebugDeclare and DebugValue with Deref +; +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1 +; CHECK: OpSLessThan +; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0 +; CHECK: OpBranch +; CHECK: OpLabel +; +; CHECK: DebugScope [[dbg_bb]] +; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0 +; +; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0 +; +; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_8 %uint_8 %uint_0 %uint_0 +; CHECK: OpReturn + +%5 = OpVariable %17 Function +OpBranch %24 +%24 = OpLabel +%35 = OpPhi %8 %10 %23 %34 %26 +%s1 = OpExtInst %6 %ext DebugScope %dbg_main +%d10 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_0 %uint_0 +%value0 = OpExtInst %6 %ext DebugValue %dbg_f %35 %null_expr +OpLoopMerge %25 %26 Unroll +OpBranch %27 +%27 = OpLabel +%s2 = OpExtInst %6 %ext DebugScope %dbg_main +%d1 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_1 %uint_1 +%29 = OpSLessThan %12 %35 %11 +%d2 = OpExtInst %6 %ext DebugLine %file_name %uint_2 %uint_2 %uint_0 %uint_0 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%s3 = OpExtInst %6 %ext DebugScope %bb +%decl0 = OpExtInst %6 %ext DebugDeclare %dbg_f %5 %null_expr +%decl1 = OpExtInst %6 %ext DebugValue %dbg_i %5 %deref_expr +%d3 = OpExtInst %6 %ext DebugLine %file_name %uint_3 %uint_3 %uint_0 %uint_0 +%32 = OpAccessChain %19 %5 %35 +%d4 = OpExtInst %6 %ext DebugLine %file_name %uint_4 %uint_4 %uint_0 %uint_0 +OpStore %32 %18 +%d5 = OpExtInst %6 %ext DebugLine %file_name %uint_5 %uint_5 %uint_0 %uint_0 +OpBranch %26 +%26 = OpLabel +%s4 = OpExtInst %6 %ext DebugScope %dbg_main +%d6 = OpExtInst %6 %ext DebugLine %file_name %uint_6 %uint_6 %uint_0 %uint_0 +%34 = OpIAdd %8 %35 %20 +%value1 = OpExtInst %6 %ext DebugValue %dbg_f %34 %null_expr +%d7 = OpExtInst %6 %ext DebugLine %file_name %uint_7 %uint_7 %uint_0 %uint_0 +OpBranch %24 +%25 = OpLabel +%s5 = OpExtInst %6 %ext DebugScope %dbg_main +%d8 = OpExtInst %6 %ext DebugLine %file_name %uint_8 %uint_8 %uint_0 %uint_0 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, true); +} + template class PartialUnrollerTestPass : public Pass { public: -- cgit v1.2.3 From c3adcb034fe4dcf77749ea6db2ad0ba91a6fc01a Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Fri, 24 Sep 2021 13:21:45 -0400 Subject: Adce refactor (NFC) (#4547) * Have ADCE use cfg struct analysis (NFC) ADCE has a lot of code and variables to keep track of information that is easily obtains using the Struct cfg analysis. Most of this change is to refactor the code to have small functions to get the information from the struct cfg analysis. A few other changes small refactoring changes are done. * Factor out work list initialization in ADCE (NFC) We move the code that will initially populate the work list into its own function. We also simplify the code by making use of the struct cfg analysis. That way we can reduce the number of tables used to track information as we traverse the CFG. --- source/opt/aggressive_dead_code_elim_pass.cpp | 361 +++++++++++++------------- source/opt/aggressive_dead_code_elim_pass.h | 63 ++--- 2 files changed, 202 insertions(+), 222 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index cb000087..ac90fd34 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -35,7 +35,6 @@ namespace { const uint32_t kTypePointerStorageClassInIdx = 0; const uint32_t kEntryPointFunctionIdInIdx = 1; const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; -const uint32_t kLoopMergeMergeBlockIdInIdx = 0; const uint32_t kLoopMergeContinueBlockIdInIdx = 1; const uint32_t kCopyMemoryTargetAddrInIdx = 0; const uint32_t kCopyMemorySourceAddrInIdx = 1; @@ -164,8 +163,7 @@ bool AggressiveDCEPass::AllExtensionsSupported() const { bool AggressiveDCEPass::IsDead(Instruction* inst) { if (IsLive(inst)) return false; if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) && - !IsStructuredHeader(context()->get_instr_block(inst), nullptr, nullptr, - nullptr)) + context()->get_instr_block(inst)->GetMergeInst() == nullptr) return false; return true; } @@ -200,66 +198,6 @@ void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { live_local_vars_.insert(varId); } -bool AggressiveDCEPass::IsStructuredHeader(BasicBlock* bp, - Instruction** mergeInst, - Instruction** branchInst, - uint32_t* mergeBlockId) { - if (!bp) return false; - Instruction* mi = bp->GetMergeInst(); - if (mi == nullptr) return false; - Instruction* bri = &*bp->tail(); - if (branchInst != nullptr) *branchInst = bri; - if (mergeInst != nullptr) *mergeInst = mi; - if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0); - return true; -} - -void AggressiveDCEPass::ComputeBlock2HeaderMaps( - std::list& structuredOrder) { - block2headerBranch_.clear(); - header2nextHeaderBranch_.clear(); - branch2merge_.clear(); - structured_order_index_.clear(); - std::stack currentHeaderBranch; - currentHeaderBranch.push(nullptr); - uint32_t currentMergeBlockId = 0; - uint32_t index = 0; - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); - ++bi, ++index) { - structured_order_index_[*bi] = index; - // If this block is the merge block of the current control construct, - // we are leaving the current construct so we must update state - if ((*bi)->id() == currentMergeBlockId) { - currentHeaderBranch.pop(); - Instruction* chb = currentHeaderBranch.top(); - if (chb != nullptr) - currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0); - } - Instruction* mergeInst; - Instruction* branchInst; - uint32_t mergeBlockId; - bool is_header = - IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId); - // Map header block to next enclosing header. - if (is_header) header2nextHeaderBranch_[*bi] = currentHeaderBranch.top(); - // If this is a loop header, update state first so the block will map to - // itself. - if (is_header && mergeInst->opcode() == SpvOpLoopMerge) { - currentHeaderBranch.push(branchInst); - branch2merge_[branchInst] = mergeInst; - currentMergeBlockId = mergeBlockId; - } - // Map the block to the current construct. - block2headerBranch_[*bi] = currentHeaderBranch.top(); - // If this is an if header, update state so following blocks map to the if. - if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) { - currentHeaderBranch.push(branchInst); - branch2merge_[branchInst] = mergeInst; - currentMergeBlockId = mergeBlockId; - } - } -} - void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) { std::unique_ptr newBranch( new Instruction(context(), SpvOpBranch, 0, 0, @@ -275,23 +213,18 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( mergeInst->opcode() == SpvOpLoopMerge); BasicBlock* header = context()->get_instr_block(mergeInst); - uint32_t headerIndex = structured_order_index_[header]; const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0); - BasicBlock* merge = context()->get_instr_block(mergeId); - uint32_t mergeIndex = structured_order_index_[merge]; - get_def_use_mgr()->ForEachUser( - mergeId, [headerIndex, mergeIndex, this](Instruction* user) { - if (!user->IsBranch()) return; - BasicBlock* block = context()->get_instr_block(user); - uint32_t index = structured_order_index_[block]; - if (headerIndex < index && index < mergeIndex) { - // This is a break from the loop. - AddToWorklist(user); - // Add branch's merge if there is one. - Instruction* userMerge = branch2merge_[user]; - if (userMerge != nullptr) AddToWorklist(userMerge); - } - }); + get_def_use_mgr()->ForEachUser(mergeId, [header, this](Instruction* user) { + if (!user->IsBranch()) return; + BasicBlock* block = context()->get_instr_block(user); + if (BlockIsInConstruct(header, block)) { + // This is a break from the loop. + AddToWorklist(user); + // Add branch's merge if there is one. + Instruction* userMerge = GetMergeInstruction(user); + if (userMerge != nullptr) AddToWorklist(userMerge); + } + }); if (mergeInst->opcode() != SpvOpLoopMerge) { return; @@ -305,7 +238,7 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( if (op == SpvOpBranchConditional || op == SpvOpSwitch) { // A conditional branch or switch can only be a continue if it does not // have a merge instruction or its merge block is not the continue block. - Instruction* hdrMerge = branch2merge_[user]; + Instruction* hdrMerge = GetMergeInstruction(user); if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) { uint32_t hdrMergeId = hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); @@ -317,9 +250,9 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( // An unconditional branch can only be a continue if it is not // branching to its own merge block. BasicBlock* blk = context()->get_instr_block(user); - Instruction* hdrBranch = block2headerBranch_[blk]; + Instruction* hdrBranch = GetHeaderBranch(blk); if (hdrBranch == nullptr) return; - Instruction* hdrMerge = branch2merge_[hdrBranch]; + Instruction* hdrMerge = GetMergeInstruction(hdrBranch); if (hdrMerge->opcode() == SpvOpLoopMerge) return; uint32_t hdrMergeId = hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); @@ -334,110 +267,14 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( bool AggressiveDCEPass::AggressiveDCE(Function* func) { // Mark function parameters as live. AddToWorklist(&func->DefInst()); - func->ForEachParam( - [this](const Instruction* param) { - AddToWorklist(const_cast(param)); - }, - false); + MarkFunctionParameterAsLive(func); - // Compute map from block to controlling conditional branch std::list structuredOrder; cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); - ComputeBlock2HeaderMaps(structuredOrder); bool modified = false; - // Add instructions with external side effects to worklist. Also add branches - // EXCEPT those immediately contained in an "if" selection construct or a loop - // or continue construct. - // TODO(greg-lunarg): Handle Frexp, Modf more optimally - call_in_func_ = false; - func_is_entry_point_ = false; - private_stores_.clear(); live_local_vars_.clear(); - // Stacks to keep track of when we are inside an if- or loop-construct. - // When immediately inside an if- or loop-construct, we do not initially - // mark branches live. All other branches must be marked live. - std::stack assume_branches_live; - std::stack currentMergeBlockId; - // Push sentinel values on stack for when outside of any control flow. - assume_branches_live.push(true); - currentMergeBlockId.push(0); - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { - // If exiting if or loop, update stacks - if ((*bi)->id() == currentMergeBlockId.top()) { - assume_branches_live.pop(); - currentMergeBlockId.pop(); - } - for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) { - SpvOp op = ii->opcode(); - switch (op) { - case SpvOpStore: { - uint32_t varId; - (void)GetPtr(&*ii, &varId); - // Mark stores as live if their variable is not function scope - // and is not private scope. Remember private stores for possible - // later inclusion. We cannot call IsLocalVar at this point because - // private_like_local_ has not been set yet. - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) - private_stores_.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) - AddToWorklist(&*ii); - } break; - case SpvOpCopyMemory: - case SpvOpCopyMemorySized: { - uint32_t varId; - (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx), - &varId); - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) - private_stores_.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) - AddToWorklist(&*ii); - } break; - case SpvOpLoopMerge: { - assume_branches_live.push(false); - currentMergeBlockId.push( - ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx)); - } break; - case SpvOpSelectionMerge: { - assume_branches_live.push(false); - currentMergeBlockId.push( - ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx)); - } break; - case SpvOpSwitch: - case SpvOpBranch: - case SpvOpBranchConditional: - case SpvOpUnreachable: { - if (assume_branches_live.top()) { - AddToWorklist(&*ii); - } - } break; - default: { - // Function calls, atomics, function params, function returns, etc. - // TODO(greg-lunarg): function calls live only if write to non-local - if (!ii->IsOpcodeSafeToDelete()) { - AddToWorklist(&*ii); - } - // Remember function calls - if (op == SpvOpFunctionCall) call_in_func_ = true; - } break; - } - } - } - // See if current function is an entry point - for (auto& ei : get_module()->entry_points()) { - if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) == - func->result_id()) { - func_is_entry_point_ = true; - break; - } - } - // If the current function is an entry point and has no function calls, - // we can optimize private variables as locals - private_like_local_ = func_is_entry_point_ && !call_in_func_; - // If privates are not like local, add their stores to worklist - if (!private_like_local_) - for (auto& ps : private_stores_) AddToWorklist(ps); + InitializeWorkList(func, structuredOrder); + // Perform closure on live instruction set. while (!worklist_.empty()) { Instruction* liveInst = worklist_.front(); @@ -469,18 +306,18 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { // If in a structured if or loop construct, add the controlling // conditional branch and its merge. BasicBlock* blk = context()->get_instr_block(liveInst); - Instruction* branchInst = block2headerBranch_[blk]; + Instruction* branchInst = GetHeaderBranch(blk); if (branchInst != nullptr) { AddToWorklist(branchInst); - Instruction* mergeInst = branch2merge_[branchInst]; + Instruction* mergeInst = GetMergeInstruction(branchInst); AddToWorklist(mergeInst); } // If the block is a header, add the next outermost controlling // conditional branch and its merge. - Instruction* nextBranchInst = header2nextHeaderBranch_[blk]; + Instruction* nextBranchInst = GetBranchForNextHeader(blk); if (nextBranchInst != nullptr) { AddToWorklist(nextBranchInst); - Instruction* mergeInst = branch2merge_[nextBranchInst]; + Instruction* mergeInst = GetMergeInstruction(nextBranchInst); AddToWorklist(mergeInst); } // If local load, add all variable's stores if variable not already live @@ -621,6 +458,89 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { return modified; } +void AggressiveDCEPass::InitializeWorkList( + Function* func, std::list& structuredOrder) { + // Add instructions with external side effects to the worklist. Also add + // branches that are not attached to a structured construct. + // TODO(s-perron): The handling of branch seems to be adhoc. This needs to be + // cleaned up. + bool call_in_func = false; + bool func_is_entry_point = false; + + // TODO(s-perron): We need to check if this is actually needed. In cases + // where private variable can be treated as if they are function scope, the + // private-to-local pass should be able to change them to function scope. + std::vector private_stores; + + for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { + for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) { + SpvOp op = ii->opcode(); + switch (op) { + case SpvOpStore: { + uint32_t varId; + (void)GetPtr(&*ii, &varId); + // Mark stores as live if their variable is not function scope + // and is not private scope. Remember private stores for possible + // later inclusion. We cannot call IsLocalVar at this point because + // private_like_local_ has not been set yet. + if (IsVarOfStorage(varId, SpvStorageClassPrivate) || + IsVarOfStorage(varId, SpvStorageClassWorkgroup)) + private_stores.push_back(&*ii); + else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) + AddToWorklist(&*ii); + } break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: { + uint32_t varId; + (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx), + &varId); + if (IsVarOfStorage(varId, SpvStorageClassPrivate) || + IsVarOfStorage(varId, SpvStorageClassWorkgroup)) + private_stores.push_back(&*ii); + else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) + AddToWorklist(&*ii); + } break; + case SpvOpSwitch: + case SpvOpBranch: + case SpvOpBranchConditional: + case SpvOpUnreachable: { + bool branchRelatedToConstruct = + (GetMergeInstruction(&*ii) == nullptr && + GetHeaderBlock(context()->get_instr_block(&*ii)) == nullptr); + if (branchRelatedToConstruct) { + AddToWorklist(&*ii); + } + } break; + case SpvOpLoopMerge: + case SpvOpSelectionMerge: + break; + default: { + // Function calls, atomics, function params, function returns, etc. + if (!ii->IsOpcodeSafeToDelete()) { + AddToWorklist(&*ii); + } + // Remember function calls + if (op == SpvOpFunctionCall) call_in_func = true; + } break; + } + } + } + // See if current function is an entry point + for (auto& ei : get_module()->entry_points()) { + if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) == + func->result_id()) { + func_is_entry_point = true; + break; + } + } + // If the current function is an entry point and has no function calls, + // we can optimize private variables as locals + private_like_local_ = func_is_entry_point && !call_in_func; + // If privates are not like local, add their stores to worklist + if (!private_like_local_) + for (auto& ps : private_stores) AddToWorklist(ps); +} + void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { // Keep all execution modes. for (auto& exec : get_module()->execution_modes()) { @@ -1027,5 +947,76 @@ void AggressiveDCEPass::InitExtensions() { }); } +Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + BasicBlock* header_block = GetHeaderBlock(blk); + if (header_block == nullptr) { + return nullptr; + } + return header_block->terminator(); +} + +BasicBlock* AggressiveDCEPass::GetHeaderBlock(BasicBlock* blk) const { + if (blk == nullptr) { + return nullptr; + } + + BasicBlock* header_block = nullptr; + if (blk->IsLoopHeader()) { + header_block = blk; + } else { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + header_block = context()->get_instr_block(header); + } + return header_block; +} + +Instruction* AggressiveDCEPass::GetMergeInstruction(Instruction* inst) { + BasicBlock* bb = context()->get_instr_block(inst); + if (bb == nullptr) { + return nullptr; + } + return bb->GetMergeInst(); +} + +Instruction* AggressiveDCEPass::GetBranchForNextHeader(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + + if (blk->IsLoopHeader()) { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + blk = context()->get_instr_block(header); + } + return GetHeaderBranch(blk); +} + +void AggressiveDCEPass::MarkFunctionParameterAsLive(const Function* func) { + func->ForEachParam( + [this](const Instruction* param) { + AddToWorklist(const_cast(param)); + }, + false); +} + +bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block, + BasicBlock* bb) { + if (bb == nullptr || header_block == nullptr) { + return false; + } + + uint32_t current_header = bb->id(); + while (current_header != 0) { + if (current_header == header_block->id()) return true; + current_header = context()->GetStructuredCFGAnalysis()->ContainingConstruct( + current_header); + } + return false; +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index abc35895..cd697b53 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h @@ -109,18 +109,6 @@ class AggressiveDCEPass : public MemPass { // If |varId| is local, mark all stores of varId as live. void ProcessLoad(Function* func, uint32_t varId); - // If |bp| is structured header block, returns true and sets |mergeInst| to - // the merge instruction, |branchInst| to the branch and |mergeBlockId| to the - // merge block if they are not nullptr. Any of |mergeInst|, |branchInst| or - // |mergeBlockId| may be a null pointer. Returns false if |bp| is a null - // pointer. - bool IsStructuredHeader(BasicBlock* bp, Instruction** mergeInst, - Instruction** branchInst, uint32_t* mergeBlockId); - - // Initialize block2headerBranch_, header2nextHeaderBranch_, and - // branch2merge_ using |structuredOrder| to order blocks. - void ComputeBlock2HeaderMaps(std::list& structuredOrder); - // Add branch to |labelId| to end of block |bp|. void AddBranch(uint32_t labelId, BasicBlock* bp); @@ -148,11 +136,33 @@ class AggressiveDCEPass : public MemPass { Pass::Status ProcessImpl(); - // True if current function has a call instruction contained in it - bool call_in_func_; + // Adds instructions which must be kept because of they have side-effects + // that ADCE cannot model to the work list. + void InitializeWorkList(Function* func, + std::list& structuredOrder); + + // Marks all of the OpFunctionParameter instructions in |func| as live. + void MarkFunctionParameterAsLive(const Function* func); + + // Returns the terminator instruction in the header for the innermost + // construct that contains |blk|. Returns nullptr if no such header exists. + Instruction* GetHeaderBranch(BasicBlock* blk); + + // Returns the header for the innermost construct that contains |blk|. A loop + // header will be its own header. Returns nullptr if no such header exists. + BasicBlock* GetHeaderBlock(BasicBlock* blk) const; + + // Returns the same as |GetHeaderBlock| except if |blk| is a loop header it + // will return the header of the next enclosing construct. Returns nullptr if + // no such header exists. + Instruction* GetBranchForNextHeader(BasicBlock* blk); + + // Returns the merge instruction in the same basic block as |inst|. Returns + // nullptr if one does not exist. + Instruction* GetMergeInstruction(Instruction* inst); - // True if current function is an entry point - bool func_is_entry_point_; + // Returns true if |bb| is in the construct with header |header_block|. + bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb); // True if current function is entry point and has no function calls. bool private_like_local_; @@ -164,27 +174,6 @@ class AggressiveDCEPass : public MemPass { // building up the live instructions set |live_insts_|. std::queue worklist_; - // Map from block to the branch instruction in the header of the most - // immediate controlling structured if or loop. A loop header block points - // to its own branch instruction. An if-selection block points to the branch - // of an enclosing construct's header, if one exists. - std::unordered_map block2headerBranch_; - - // Map from header block to the branch instruction in the header of the - // structured construct enclosing it. - // The liveness algorithm is designed to iteratively mark as live all - // structured constructs enclosing a live instruction. - std::unordered_map header2nextHeaderBranch_; - - // Maps basic block to their index in the structured order traversal. - std::unordered_map structured_order_index_; - - // Map from branch to its associated merge instruction, if any - std::unordered_map branch2merge_; - - // Store instructions to variables of private storage - std::vector private_stores_; - // Live Instructions utils::BitVector live_insts_; -- cgit v1.2.3 From ba4b390c367e09edaad36f0376a87aa03620fd4b Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 28 Sep 2021 15:52:52 +0100 Subject: Suppress protobuf warning (#4551) Suppresses a warning emitted by some versions of clang when compiling protobufs. --- source/fuzz/protobufs/spirvfuzz_protobufs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h index 429f341e..46c21881 100644 --- a/source/fuzz/protobufs/spirvfuzz_protobufs.h +++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -24,6 +24,7 @@ #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-warning-option" // Must come first +#pragma clang diagnostic ignored "-Wreserved-identifier" #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wsuggest-destructor-override" #pragma clang diagnostic ignored "-Wunused-parameter" -- cgit v1.2.3 From 3234daada7d3e8f2030f84d620995e5d0625058e Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 30 Sep 2021 19:20:28 -0400 Subject: Do not assume there are execution modes (#4555) Fixes #4550 * Do not assume that an entry point has any associated execution modes when checking derivative requirements --- source/val/validate_derivatives.cpp | 12 +++++++----- test/val/val_derivatives_test.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/source/val/validate_derivatives.cpp b/source/val/validate_derivatives.cpp index 067cc964..25b941ab 100644 --- a/source/val/validate_derivatives.cpp +++ b/source/val/validate_derivatives.cpp @@ -79,11 +79,13 @@ spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) { std::string* message) { const auto* models = state.GetExecutionModels(entry_point->id()); const auto* modes = state.GetExecutionModes(entry_point->id()); - if (models->find(SpvExecutionModelGLCompute) != models->end() && - modes->find(SpvExecutionModeDerivativeGroupLinearNV) == - modes->end() && - modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == - modes->end()) { + if (models && + models->find(SpvExecutionModelGLCompute) != models->end() && + (!modes || + (modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()))) { if (message) { *message = std::string( "Derivative instructions require " diff --git a/test/val/val_derivatives_test.cpp b/test/val/val_derivatives_test.cpp index 606abb93..0a846610 100644 --- a/test/val/val_derivatives_test.cpp +++ b/test/val/val_derivatives_test.cpp @@ -160,6 +160,31 @@ TEST_F(ValidateDerivatives, OpDPdxWrongExecutionModel) { "execution model: DPdx")); } +TEST_F(ValidateDerivatives, NoExecutionModeGLCompute) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%undef = OpUndef %float4 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%derivative = OpDPdy %float4 %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Derivative instructions require " + "DerivativeGroupQuadsNV or DerivativeGroupLinearNV " + "execution mode for GLCompute execution model")); +} + using ValidateHalfDerivatives = spvtest::ValidateBase; TEST_P(ValidateHalfDerivatives, ScalarFailure) { -- cgit v1.2.3 From 24476c2e32ff809bcca0a8a541ac34014c4cacd3 Mon Sep 17 00:00:00 2001 From: Lukas Hermanns Date: Fri, 1 Oct 2021 08:31:40 -0400 Subject: spirv-opt: Don't eliminate dead members from StructuredBuffer (#4553) * Don't eliminate dead members from StructuredBuffer as layout(offset) qualifiers cannot be applied to structure fields. * Traverse arrays when marking structs as fully used. Co-authored-by: Steven Perron --- source/opt/eliminate_dead_members_pass.cpp | 31 +++++++++------ source/opt/instruction.cpp | 16 ++++++++ source/opt/instruction.h | 4 ++ test/opt/eliminate_dead_member_test.cpp | 61 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp index 173df620..a24ba8f4 100644 --- a/source/opt/eliminate_dead_members_pass.cpp +++ b/source/opt/eliminate_dead_members_pass.cpp @@ -20,6 +20,7 @@ namespace { const uint32_t kRemovedMember = 0xFFFFFFFF; const uint32_t kSpecConstOpOpcodeIdx = 0; +constexpr uint32_t kArrayElementTypeIdx = 0; } // namespace namespace spvtools { @@ -64,6 +65,10 @@ void EliminateDeadMembersPass::FindLiveMembers() { MarkPointeeTypeAsFullUsed(inst.type_id()); break; default: + // Ignore structured buffers as layout(offset) qualifiers cannot be + // applied to structure fields + if (inst.IsVulkanStorageBufferVariable()) + MarkPointeeTypeAsFullUsed(inst.type_id()); break; } } @@ -136,18 +141,22 @@ void EliminateDeadMembersPass::MarkMembersAsLiveForStore( void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); assert(type_inst != nullptr); - if (type_inst->opcode() != SpvOpTypeStruct) { - return; - } - - // Mark every member of the current struct as used. - for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { - used_members_[type_id].insert(i); - } - // Mark any sub struct as fully used. - for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { - MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: + // Mark every member and its type as fully used. + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + used_members_[type_id].insert(i); + MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + } + break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + MarkTypeAsFullyUsed( + type_inst->GetSingleWordInOperand(kArrayElementTypeIdx)); + break; + default: + break; } } diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 665b72f0..2461e41e 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -30,6 +30,7 @@ namespace { const uint32_t kTypeImageDimIndex = 1; const uint32_t kLoadBaseIndex = 0; const uint32_t kPointerTypeStorageClassIndex = 0; +const uint32_t kVariableStorageClassIndex = 0; const uint32_t kTypeImageSampledIndex = 5; // Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 @@ -403,6 +404,21 @@ bool Instruction::IsVulkanStorageBuffer() const { return false; } +bool Instruction::IsVulkanStorageBufferVariable() const { + if (opcode() != SpvOpVariable) { + return false; + } + + uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex); + if (storage_class == SpvStorageClassStorageBuffer || + storage_class == SpvStorageClassUniform) { + Instruction* var_type = context()->get_def_use_mgr()->GetDef(type_id()); + return var_type != nullptr && var_type->IsVulkanStorageBuffer(); + } + + return false; +} + bool Instruction::IsVulkanUniformBuffer() const { if (opcode() != SpvOpTypePointer) { return false; diff --git a/source/opt/instruction.h b/source/opt/instruction.h index c962e509..ce568f66 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -464,6 +464,10 @@ class Instruction : public utils::IntrusiveNodeBase { // storage buffer. bool IsVulkanStorageBuffer() const; + // Returns true if the instruction defines a variable in StorageBuffer or + // Uniform storage class with a pointer type that points to a storage buffer. + bool IsVulkanStorageBufferVariable() const; + // Returns true if the instruction defines a pointer type that points to a // uniform buffer. bool IsVulkanUniformBuffer() const; diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp index 78874330..e277999e 100644 --- a/test/opt/eliminate_dead_member_test.cpp +++ b/test/opt/eliminate_dead_member_test.cpp @@ -626,6 +626,67 @@ TEST_F(EliminateDeadMemberTest, KeepMembersOpStore) { EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); } +TEST_F(EliminateDeadMemberTest, KeepStorageBufferMembers) { + // Test that all members of the storage buffer struct %S are kept. + // No change expected. + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 600 + OpName %type_StructuredBuffer_S "type.StructuredBuffer.S" + OpName %S "S" + OpMemberName %S 0 "A" + OpMemberName %S 1 "B" + OpName %Buf "Buf" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpDecorateString %out_var_SV_TARGET UserSemantic "SV_TARGET" + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Buf DescriptorSet 0 + OpDecorate %Buf Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpDecorate %_runtimearr_S ArrayStride 32 + OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable + OpDecorate %type_StructuredBuffer_S BufferBlock + OpDecorateString %Buf UserTypeGOOGLE "structuredbuffer" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %S = OpTypeStruct %v4float %v4float +%_runtimearr_S = OpTypeRuntimeArray %S +%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %Buf = OpVariable %_ptr_Uniform_type_StructuredBuffer_S Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %18 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v4float %Buf %int_0 %uint_0 %int_1 + %22 = OpLoad %v4float %21 + OpStore %out_var_SV_TARGET %22 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemory) { // Test that all members are kept because of an OpCopyMemory. // No change expected. -- cgit v1.2.3 From 9529d3c2c647bf7c7aa1a2601d0209da98f8fa1e Mon Sep 17 00:00:00 2001 From: David Neto Date: Fri, 1 Oct 2021 08:43:59 -0400 Subject: Avoid implicit fallthrough, by duplicating code (#4556) The cleverness is probably not worth it. --- source/text_handler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/text_handler.cpp b/source/text_handler.cpp index c31f34a6..46b98456 100644 --- a/source/text_handler.cpp +++ b/source/text_handler.cpp @@ -120,7 +120,8 @@ spv_result_t getWord(spv_text text, spv_position position, std::string* word) { case '\n': case '\r': if (escaping || quoting) break; - // Fall through. + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; case '\0': { // NOTE: End of word found! word->assign(text->str + start_index, text->str + position->index); return SPV_SUCCESS; -- cgit v1.2.3 From eeb973f5020a5f0e92ad6da879bc4df9f5985a1c Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 4 Oct 2021 08:33:10 -0400 Subject: More ADCE refactoring (#4548) Split the code that processes the work list into multiple functions. Move the code to remove the dead instructions in a function to its own function. --- source/opt/aggressive_dead_code_elim_pass.cpp | 378 ++++++++++++++------------ source/opt/aggressive_dead_code_elim_pass.h | 45 ++- source/opt/mem_pass.cpp | 2 +- 3 files changed, 254 insertions(+), 171 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index ac90fd34..1f922536 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -38,6 +38,7 @@ const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; const uint32_t kLoopMergeContinueBlockIdInIdx = 1; const uint32_t kCopyMemoryTargetAddrInIdx = 0; const uint32_t kCopyMemorySourceAddrInIdx = 1; +const uint32_t kLoadSourceAddrInIdx = 0; const uint32_t kDebugDeclareOperandVariableIndex = 5; const uint32_t kGlobalVariableVariableIndex = 12; @@ -265,170 +266,36 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( } bool AggressiveDCEPass::AggressiveDCE(Function* func) { - // Mark function parameters as live. - AddToWorklist(&func->DefInst()); - MarkFunctionParameterAsLive(func); - - std::list structuredOrder; - cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); - bool modified = false; + std::list structured_order; + cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order); live_local_vars_.clear(); - InitializeWorkList(func, structuredOrder); - - // Perform closure on live instruction set. - while (!worklist_.empty()) { - Instruction* liveInst = worklist_.front(); - // Add all operand instructions of Debug[No]Lines - for (auto& lineInst : liveInst->dbg_line_insts()) { - if (lineInst.IsDebugLineInst()) { - lineInst.ForEachInId([this](const uint32_t* iid) { - AddToWorklist(get_def_use_mgr()->GetDef(*iid)); - }); - } - } - // Add all operand instructions if not already live - liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) { - Instruction* inInst = get_def_use_mgr()->GetDef(*iid); - // Do not add label if an operand of a branch. This is not needed - // as part of live code discovery and can create false live code, - // for example, the branch to a header of a loop. - if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return; - AddToWorklist(inInst); - }); - if (liveInst->type_id() != 0) { - AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id())); - } - BasicBlock* basic_block = context()->get_instr_block(liveInst); - if (basic_block != nullptr) { - AddToWorklist(basic_block->GetLabelInst()); - } - - // If in a structured if or loop construct, add the controlling - // conditional branch and its merge. - BasicBlock* blk = context()->get_instr_block(liveInst); - Instruction* branchInst = GetHeaderBranch(blk); - if (branchInst != nullptr) { - AddToWorklist(branchInst); - Instruction* mergeInst = GetMergeInstruction(branchInst); - AddToWorklist(mergeInst); - } - // If the block is a header, add the next outermost controlling - // conditional branch and its merge. - Instruction* nextBranchInst = GetBranchForNextHeader(blk); - if (nextBranchInst != nullptr) { - AddToWorklist(nextBranchInst); - Instruction* mergeInst = GetMergeInstruction(nextBranchInst); - AddToWorklist(mergeInst); - } - // If local load, add all variable's stores if variable not already live - if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) { - uint32_t varId; - (void)GetPtr(liveInst, &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - // Process memory copies like loads - } else if (liveInst->opcode() == SpvOpCopyMemory || - liveInst->opcode() == SpvOpCopyMemorySized) { - uint32_t varId; - (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx), - &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - // If DebugDeclare, process as load of variable - } else if (liveInst->GetCommonDebugOpcode() == - CommonDebugInfoDebugDeclare) { - uint32_t varId = - liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); - ProcessLoad(func, varId); - // If DebugValue with Deref, process as load of variable - } else if (liveInst->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { - uint32_t varId = context() - ->get_debug_info_mgr() - ->GetVariableIdOfDebugValueUsedForDeclare(liveInst); - if (varId != 0) ProcessLoad(func, varId); - // If merge, add other branches that are part of its control structure - } else if (liveInst->opcode() == SpvOpLoopMerge || - liveInst->opcode() == SpvOpSelectionMerge) { - AddBreaksAndContinuesToWorklist(liveInst); - // If function call, treat as if it loads from all pointer arguments - } else if (liveInst->opcode() == SpvOpFunctionCall) { - liveInst->ForEachInId([this, func](const uint32_t* iid) { - // Skip non-ptr args - if (!IsPtr(*iid)) return; - uint32_t varId; - (void)GetPtr(*iid, &varId); - ProcessLoad(func, varId); - }); - // If function parameter, treat as if it's result id is loaded from - } else if (liveInst->opcode() == SpvOpFunctionParameter) { - ProcessLoad(func, liveInst->result_id()); - // We treat an OpImageTexelPointer as a load of the pointer, and - // that value is manipulated to get the result. - } else if (liveInst->opcode() == SpvOpImageTexelPointer) { - uint32_t varId; - (void)GetPtr(liveInst, &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - } - - // Add OpDecorateId instructions that apply to this instruction to the work - // list. We use the decoration manager to look through the group - // decorations to get to the OpDecorate* instructions themselves. - auto decorations = - get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false); - for (Instruction* dec : decorations) { - // We only care about OpDecorateId instructions because the are the only - // decorations that will reference an id that will have to be kept live - // because of that use. - if (dec->opcode() != SpvOpDecorateId) { - continue; - } - if (dec->GetSingleWordInOperand(1) == - SpvDecorationHlslCounterBufferGOOGLE) { - // These decorations should not force the use id to be live. It will be - // removed if either the target or the in operand are dead. - continue; - } - AddToWorklist(dec); - } - - // Add DebugScope and DebugInlinedAt for |liveInst| to the work list. - if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { - auto* scope = get_def_use_mgr()->GetDef( - liveInst->GetDebugScope().GetLexicalScope()); - AddToWorklist(scope); - } - if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) { - auto* inlined_at = - get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt()); - AddToWorklist(inlined_at); - } - worklist_.pop(); - } + InitializeWorkList(func, structured_order); + ProcessWorkList(func); + return KillDeadInstructions(func, structured_order); +} - // Kill dead instructions and remember dead blocks - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) { - uint32_t mergeBlockId = 0; - (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) { +bool AggressiveDCEPass::KillDeadInstructions( + const Function* func, std::list& structured_order) { + bool modified = false; + for (auto bi = structured_order.begin(); bi != structured_order.end();) { + uint32_t merge_block_id = 0; + (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) { if (!IsDead(inst)) return; if (inst->opcode() == SpvOpLabel) return; // If dead instruction is selection merge, remember merge block // for new branch at end of block if (inst->opcode() == SpvOpSelectionMerge || inst->opcode() == SpvOpLoopMerge) - mergeBlockId = inst->GetSingleWordInOperand(0); + merge_block_id = inst->GetSingleWordInOperand(0); to_kill_.push_back(inst); modified = true; }); // If a structured if or loop was deleted, add a branch to its merge // block, and traverse to the merge block and continue processing there. // We know the block still exists because the label is not deleted. - if (mergeBlockId != 0) { - AddBranch(mergeBlockId, *bi); - for (++bi; (*bi)->id() != mergeBlockId; ++bi) { + if (merge_block_id != 0) { + AddBranch(merge_block_id, *bi); + for (++bi; (*bi)->id() != merge_block_id; ++bi) { } auto merge_terminator = (*bi)->terminator(); @@ -454,12 +321,185 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { ++bi; } } - return modified; } +void AggressiveDCEPass::ProcessWorkList(Function* func) { + while (!worklist_.empty()) { + Instruction* live_inst = worklist_.front(); + worklist_.pop(); + AddOperandsToWorkList(live_inst); + MarkBlockAsLive(live_inst); + MarkLoadedVariablesAsLive(func, live_inst); + AddDecorationsToWorkList(live_inst); + AddDebugInstructionsToWorkList(live_inst); + } +} + +void AggressiveDCEPass::AddDebugInstructionsToWorkList( + const Instruction* inst) { + for (auto& line_inst : inst->dbg_line_insts()) { + if (line_inst.IsDebugLineInst()) { + AddOperandsToWorkList(&line_inst); + } + } + + if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { + auto* scope = + get_def_use_mgr()->GetDef(inst->GetDebugScope().GetLexicalScope()); + AddToWorklist(scope); + } + if (inst->GetDebugInlinedAt() != kNoInlinedAt) { + auto* inlined_at = get_def_use_mgr()->GetDef(inst->GetDebugInlinedAt()); + AddToWorklist(inlined_at); + } +} + +void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) { + // Add OpDecorateId instructions that apply to this instruction to the work + // list. We use the decoration manager to look through the group + // decorations to get to the OpDecorate* instructions themselves. + auto decorations = + get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false); + for (Instruction* dec : decorations) { + // We only care about OpDecorateId instructions because the are the only + // decorations that will reference an id that will have to be kept live + // because of that use. + if (dec->opcode() != SpvOpDecorateId) { + continue; + } + if (dec->GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + // These decorations should not force the use id to be live. It will be + // removed if either the target or the in operand are dead. + continue; + } + AddToWorklist(dec); + } +} + +void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func, + Instruction* inst) { + std::vector live_variables = GetLoadedVariables(inst); + for (uint32_t var_id : live_variables) { + ProcessLoad(func, var_id); + } +} + +std::vector AggressiveDCEPass::GetLoadedVariables(Instruction* inst) { + if (inst->opcode() == SpvOpFunctionCall) { + return GetLoadedVariablesFromFunctionCall(inst); + } + uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst); + if (var_id == 0) { + return {}; + } + return {var_id}; +} + +uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls( + Instruction* inst) { + std::vector live_variables; + if (inst->IsAtomicWithLoad()) { + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + } + + switch (inst->opcode()) { + case SpvOpLoad: + case SpvOpImageTexelPointer: + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + return GetVariableId( + inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx)); + default: + break; + } + + switch (inst->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: + return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + case CommonDebugInfoDebugValue: { + analysis::DebugInfoManager* debug_info_mgr = + context()->get_debug_info_mgr(); + return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst); + } + default: + break; + } + return 0; +} + +std::vector AggressiveDCEPass::GetLoadedVariablesFromFunctionCall( + const Instruction* inst) { + assert(inst->opcode() == SpvOpFunctionCall); + std::vector live_variables; + inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) { + if (!IsPtr(*operand_id)) return; + uint32_t var_id = GetVariableId(*operand_id); + live_variables.push_back(var_id); + }); + return live_variables; +} + +uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) { + assert(IsPtr(ptr_id) && + "Cannot get the variable when input is not a pointer."); + uint32_t varId = 0; + (void)GetPtr(ptr_id, &varId); + return varId; +} + +void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) { + BasicBlock* basic_block = context()->get_instr_block(inst); + if (basic_block != nullptr) { + AddToWorklist(basic_block->GetLabelInst()); + } + + // If in a structured if or loop construct, add the controlling + // conditional branch and its merge. + BasicBlock* blk = context()->get_instr_block(inst); + Instruction* branchInst = GetHeaderBranch(blk); + if (branchInst != nullptr) { + AddToWorklist(branchInst); + Instruction* mergeInst = GetMergeInstruction(branchInst); + AddToWorklist(mergeInst); + } + + // If the block is a header, add the next outermost controlling + // conditional branch and its merge. + Instruction* nextBranchInst = GetBranchForNextHeader(blk); + if (nextBranchInst != nullptr) { + AddToWorklist(nextBranchInst); + Instruction* mergeInst = GetMergeInstruction(nextBranchInst); + AddToWorklist(mergeInst); + } + + if (inst->opcode() == SpvOpLoopMerge || + inst->opcode() == SpvOpSelectionMerge) { + AddBreaksAndContinuesToWorklist(inst); + } +} + +void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) { + inst->ForEachInId([&inst, this](const uint32_t* iid) { + Instruction* inInst = get_def_use_mgr()->GetDef(*iid); + // Do not add label if an operand of a branch. This is not needed + // as part of live code discovery and can create false live code, + // for example, the branch to a header of a loop. + if (inInst->opcode() == SpvOpLabel && inst->IsBranch()) return; + AddToWorklist(inInst); + }); + if (inst->type_id() != 0) { + AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id())); + } +} + void AggressiveDCEPass::InitializeWorkList( - Function* func, std::list& structuredOrder) { + Function* func, std::list& structured_order) { + AddToWorklist(&func->DefInst()); + MarkFunctionParameterAsLive(func); + // Add instructions with external side effects to the worklist. Also add // branches that are not attached to a structured construct. // TODO(s-perron): The handling of branch seems to be adhoc. This needs to be @@ -472,42 +512,42 @@ void AggressiveDCEPass::InitializeWorkList( // private-to-local pass should be able to change them to function scope. std::vector private_stores; - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { - for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) { + for (auto& bi : structured_order) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { SpvOp op = ii->opcode(); switch (op) { case SpvOpStore: { - uint32_t varId; - (void)GetPtr(&*ii, &varId); + uint32_t var_id = 0; + (void)GetPtr(&*ii, &var_id); // Mark stores as live if their variable is not function scope // and is not private scope. Remember private stores for possible // later inclusion. We cannot call IsLocalVar at this point because // private_like_local_ has not been set yet. - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) + if (IsVarOfStorage(var_id, SpvStorageClassPrivate) || + IsVarOfStorage(var_id, SpvStorageClassWorkgroup)) private_stores.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) + else if (!IsVarOfStorage(var_id, SpvStorageClassFunction)) AddToWorklist(&*ii); } break; case SpvOpCopyMemory: case SpvOpCopyMemorySized: { - uint32_t varId; + uint32_t var_id = 0; (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx), - &varId); - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) + &var_id); + if (IsVarOfStorage(var_id, SpvStorageClassPrivate) || + IsVarOfStorage(var_id, SpvStorageClassWorkgroup)) private_stores.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) + else if (!IsVarOfStorage(var_id, SpvStorageClassFunction)) AddToWorklist(&*ii); } break; case SpvOpSwitch: case SpvOpBranch: case SpvOpBranchConditional: case SpvOpUnreachable: { - bool branchRelatedToConstruct = + bool branch_related_to_construct = (GetMergeInstruction(&*ii) == nullptr && GetHeaderBlock(context()->get_instr_block(&*ii)) == nullptr); - if (branchRelatedToConstruct) { + if (branch_related_to_construct) { AddToWorklist(&*ii); } } break; @@ -601,9 +641,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; dbg.ForEachInId([this](const uint32_t* iid) { - Instruction* inInst = get_def_use_mgr()->GetDef(*iid); - if (inInst->opcode() == SpvOpVariable) return; - AddToWorklist(inInst); + Instruction* in_inst = get_def_use_mgr()->GetDef(*iid); + if (in_inst->opcode() == SpvOpVariable) return; + AddToWorklist(in_inst); }); } } diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index cd697b53..e0a9b524 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h @@ -139,7 +139,50 @@ class AggressiveDCEPass : public MemPass { // Adds instructions which must be kept because of they have side-effects // that ADCE cannot model to the work list. void InitializeWorkList(Function* func, - std::list& structuredOrder); + std::list& structured_order); + + // Process each instruction in the work list by marking any instruction that + // that it depends on as live, and adding it to the work list. The work list + // will be empty at the end. + void ProcessWorkList(Function* func); + + // Kills any instructions in |func| that have not been marked as live. + bool KillDeadInstructions(const Function* func, + std::list& structured_order); + + // Adds the instructions that define the operands of |inst| to the work list. + void AddOperandsToWorkList(const Instruction* inst); + + // Marks all of the labels and branch that inst requires as live. + void MarkBlockAsLive(Instruction* inst); + + // Marks any variables from which |inst| may require data as live. + void MarkLoadedVariablesAsLive(Function* opernad_id, Instruction* inst); + + // Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a + // value whose type is a pointer. + uint32_t GetVariableId(uint32_t ptr_id); + + // Returns all of the ids for the variables from which |inst| will load data. + std::vector GetLoadedVariables(Instruction* inst); + + // Returns all of the ids for the variables from which |inst| will load data. + // The opcode of |inst| must be OpFunctionCall. + std::vector GetLoadedVariablesFromFunctionCall( + const Instruction* inst); + + // Returns the id of the variable from which |inst| will load data. |inst| + // must not be an OpFunctionCall. Returns 0 if no data is read or the + // variable cannot be determined. Note that in logical addressing mode the + // latter is not possible for function and private storage class because there + // cannot be variable pointers pointing to those storage classes. + uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst); + + // Adds all decorations of |inst| to the work list. + void AddDecorationsToWorkList(const Instruction* inst); + + // Adds all debug instruction associated with |inst| to the work list. + void AddDebugInstructionsToWorkList(const Instruction* inst); // Marks all of the OpFunctionParameter instructions in |func| as live. void MarkFunctionParameterAsLive(const Function* func); diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp index 72442a99..ca4889b7 100644 --- a/source/opt/mem_pass.cpp +++ b/source/opt/mem_pass.cpp @@ -86,8 +86,8 @@ bool MemPass::IsPtr(uint32_t ptrId) { } const SpvOp op = ptrInst->opcode(); if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true; - if (op != SpvOpFunctionParameter) return false; const uint32_t varTypeId = ptrInst->type_id(); + if (varTypeId == 0) return false; const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); return varTypeInst->opcode() == SpvOpTypePointer; } -- cgit v1.2.3 From 2d12367ced2dd34444822340070b4545ae7c02f1 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Mon, 4 Oct 2021 13:42:12 -0400 Subject: Stop consuming input in fuzzers to select target environment (#4544) Instead calculate a hash based on the input and use that as a seed into random data generation for the target env. Also fixes issue where input data was not actually being fed into one fuzzer. Fixes #4450 --- source/spirv_target_env.cpp | 32 ++++++ source/spirv_target_env.h | 3 + test/fuzzers/BUILD.gn | 1 + test/fuzzers/CMakeLists.txt | 14 +-- test/fuzzers/random_generator.cpp | 133 ++++++++++++++++++++++ test/fuzzers/random_generator.h | 57 ++++++++++ test/fuzzers/spvtools_as_fuzzer.cpp | 23 ++-- test/fuzzers/spvtools_binary_parser_fuzzer.cpp | 16 +-- test/fuzzers/spvtools_dis_fuzzer.cpp | 21 ++-- test/fuzzers/spvtools_opt_legalization_fuzzer.cpp | 8 +- test/fuzzers/spvtools_opt_performance_fuzzer.cpp | 8 +- test/fuzzers/spvtools_opt_size_fuzzer.cpp | 8 +- test/fuzzers/spvtools_val_fuzzer.cpp | 8 +- 13 files changed, 293 insertions(+), 39 deletions(-) create mode 100644 test/fuzzers/random_generator.cpp create mode 100644 test/fuzzers/random_generator.h diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index f20ebb4f..1ff1cbf1 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -286,6 +286,38 @@ bool spvIsOpenGLEnv(spv_target_env env) { return false; } +bool spvIsValidEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return true; + case SPV_ENV_WEBGPU_0: + break; + } + return false; +} + std::string spvLogStringForEnv(spv_target_env env) { switch (env) { case SPV_ENV_OPENCL_1_2: diff --git a/source/spirv_target_env.h b/source/spirv_target_env.h index a804d615..cc06deca 100644 --- a/source/spirv_target_env.h +++ b/source/spirv_target_env.h @@ -28,6 +28,9 @@ bool spvIsOpenCLEnv(spv_target_env env); // Returns true if |env| is an OPENGL environment, false otherwise. bool spvIsOpenGLEnv(spv_target_env env); +// Returns true if |env| is an implemented/valid environment, false otherwise. +bool spvIsValidEnv(spv_target_env env); + // Returns the version number for the given SPIR-V target environment. uint32_t spvVersionForTargetEnv(spv_target_env env); diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn index ec09b2b0..2aef4e8f 100644 --- a/test/fuzzers/BUILD.gn +++ b/test/fuzzers/BUILD.gn @@ -48,6 +48,7 @@ template("spvtools_fuzzer") { source_set(target_name) { testonly = true sources = invoker.sources + sources += [ "random_generator.cpp" ] deps = [ "../..:spvtools", "../..:spvtools_opt", diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt index afcc6b8f..a97c8db1 100644 --- a/test/fuzzers/CMakeLists.txt +++ b/test/fuzzers/CMakeLists.txt @@ -43,11 +43,11 @@ if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") message(FATAL_ERROR "The libFuzzer targets are only supported with the Clang compiler. Compiler '${CMAKE_CXX_COMPILER_ID}' is not supported!") endif() - add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) endif() diff --git a/test/fuzzers/random_generator.cpp b/test/fuzzers/random_generator.cpp new file mode 100644 index 00000000..0e932716 --- /dev/null +++ b/test/fuzzers/random_generator.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/fuzzers/random_generator.h" + +#include +#include +#include + +namespace spvtools { +namespace fuzzers { + +namespace { +/// Generate integer from uniform distribution +/// @tparam I - integer type +/// @param engine - random number engine to use +/// @param lower - Lower bound of integer generated +/// @param upper - Upper bound of integer generated +/// @returns i, where lower <= i < upper +template +I RandomUInt(std::mt19937* engine, I lower, I upper) { + assert(lower < upper && "|lower| must be stictly less than |upper|"); + return std::uniform_int_distribution(lower, upper - 1)(*engine); +} + +/// Helper for obtaining a seed bias value for HashCombine with a bit-width +/// dependent on the size of size_t. +template +struct HashCombineOffset {}; +/// Specialization of HashCombineOffset for size_t == 4. +template <> +struct HashCombineOffset<4> { + /// @returns the seed bias value for HashCombine() + static constexpr inline uint32_t value() { + return 0x9e3779b9; // Fractional portion of Golden Ratio, suggested by + // Linux Kernel and Knuth's Art of Computer Programming + } +}; +/// Specialization of HashCombineOffset for size_t == 8. +template <> +struct HashCombineOffset<8> { + /// @returns the seed bias value for HashCombine() + static constexpr inline uint64_t value() { + return 0x9e3779b97f4a7c16; // Fractional portion of Golden Ratio, suggested + // by Linux Kernel and Knuth's Art of Computer + // Programming + } +}; + +/// HashCombine "hashes" together an existing hash and hashable values. +template +void HashCombine(size_t* hash, const T& value) { + constexpr size_t offset = HashCombineOffset::value(); + *hash ^= std::hash()(value) + offset + (*hash << 6) + (*hash >> 2); +} + +/// Calculate the hash for the contents of a C-style data buffer +/// @param data - pointer to buffer to be hashed +/// @param size - number of elements in buffer +/// @returns hash of the data in the buffer +size_t HashBuffer(const uint8_t* data, const size_t size) { + size_t hash = 0xCA8945571519E991; // seed with an arbitrary prime + HashCombine(&hash, size); + for (size_t i = 0; i < size; i++) { + HashCombine(&hash, data[i]); + } + return hash; +} + +} // namespace + +RandomGenerator::RandomGenerator(uint64_t seed) : engine_(seed) {} + +RandomGenerator::RandomGenerator(const uint8_t* data, size_t size) { + RandomGenerator(RandomGenerator::CalculateSeed(data, size)); +} + +spv_target_env RandomGenerator::GetTargetEnv() { + // SPV_ENV_WEBGPU_0 is intentionally omitted here, since it is deprecated and + // using it will cause asserts. + static const std::array envs = { + SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_0, + SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, + SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_EMBEDDED_1_2, + SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_EMBEDDED_2_0, + SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_EMBEDDED_2_2, + SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2, + SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, + SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_2, + SPV_ENV_OPENGL_4_0, SPV_ENV_OPENGL_4_1, + SPV_ENV_OPENGL_4_2, SPV_ENV_OPENGL_4_3, + SPV_ENV_OPENGL_4_5}; + + return envs[RandomUInt(&engine_, 0lu, envs.size())]; +} + +uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) { + assert(data != nullptr && "|data| must be !nullptr"); + + // Number of bytes we want to skip at the start of data for the hash. + // Fewer bytes may be skipped when `size` is small. + // Has lower precedence than kHashDesiredMinBytes. + static const int64_t kHashDesiredLeadingSkipBytes = 5; + // Minimum number of bytes we want to use in the hash. + // Used for short buffers. + static const int64_t kHashDesiredMinBytes = 4; + // Maximum number of bytes we want to use in the hash. + static const int64_t kHashDesiredMaxBytes = 32; + int64_t size_i64 = static_cast(size); + int64_t hash_begin_i64 = + std::min(kHashDesiredLeadingSkipBytes, + std::max(size_i64 - kHashDesiredMinBytes, 0)); + int64_t hash_end_i64 = + std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64); + size_t hash_begin = static_cast(hash_begin_i64); + size_t hash_size = static_cast(hash_end_i64) - hash_begin; + return HashBuffer(data + hash_begin, hash_size); +} + +} // namespace fuzzers +} // namespace spvtools diff --git a/test/fuzzers/random_generator.h b/test/fuzzers/random_generator.h new file mode 100644 index 00000000..b9fc2759 --- /dev/null +++ b/test/fuzzers/random_generator.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_FUZZERS_RANDOM_GENERATOR_H_ +#define TEST_FUZZERS_RANDOM_GENERATOR_H_ + +#include +#include + +#include "source/spirv_target_env.h" + +namespace spvtools { +namespace fuzzers { + +/// Pseudo random generator utility class for fuzzing +class RandomGenerator { + public: + /// @brief Initializes the internal engine + /// @param seed - seed value passed to engine + explicit RandomGenerator(uint64_t seed); + + /// @brief Initializes the internal engine + /// @param data - data to calculate the seed from + /// @param size - size of the data + explicit RandomGenerator(const uint8_t* data, size_t size); + + ~RandomGenerator() {} + + /// Calculate a seed value based on a blob of data. + /// Currently hashes bytes near the front of the buffer, after skipping N + /// bytes. + /// @param data - pointer to data to base calculation off of, must be !nullptr + /// @param size - number of elements in |data|, must be > 0 + static uint64_t CalculateSeed(const uint8_t* data, size_t size); + + /// Get random target env. + spv_target_env GetTargetEnv(); + + private: + std::mt19937 engine_; +}; // class RandomGenerator + +} // namespace fuzzers +} // namespace spvtools + +#endif // TEST_FUZZERS_RANDOM_GENERATOR_UTILS_H_ diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp index 8cecb05f..ba3f5b97 100644 --- a/test/fuzzers/spvtools_as_fuzzer.cpp +++ b/test/fuzzers/spvtools_as_fuzzer.cpp @@ -18,18 +18,27 @@ #include "source/spirv_target_env.h" #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; - - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0; + if (size > 0) { + spvtools::fuzzers::RandomGenerator random_gen(data, size); + target_env = random_gen.GetTargetEnv(); + } - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); + const spv_context context = spvContextCreate(target_env); + if (context == nullptr) { + return 0; + } std::vector input; + input.resize(size >> 2); + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } std::vector input_str; size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char); diff --git a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp index 76ba4d9e..3a97db41 100644 --- a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp +++ b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp @@ -16,16 +16,18 @@ #include #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; - - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; + if (size < 1) { + return 0; + } - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); + spvtools::fuzzers::RandomGenerator random_gen(data, size); + const spv_context context = spvContextCreate(random_gen.GetTargetEnv()); + if (context == nullptr) { + return 0; + } std::vector input; input.resize(size >> 2); diff --git a/test/fuzzers/spvtools_dis_fuzzer.cpp b/test/fuzzers/spvtools_dis_fuzzer.cpp index 5a727221..0cb0eff5 100644 --- a/test/fuzzers/spvtools_dis_fuzzer.cpp +++ b/test/fuzzers/spvtools_dis_fuzzer.cpp @@ -18,28 +18,21 @@ #include "source/spirv_target_env.h" #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; - - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4450): A more - // general solution to choosing the target environment based on the input - // buffer should ultimately be used. - uint32_t first_data_word = *reinterpret_cast(data); - spv_target_env target_env = static_cast( - first_data_word % (static_cast(SPV_ENV_VULKAN_1_2) + 1)); - const spv_context context = spvContextCreate(target_env); - if (context == nullptr) return 0; - - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); - if (size < 4) { // There are not enough bytes to constitute a binary that can be // disassembled. return 0; } + spvtools::fuzzers::RandomGenerator random_gen(data, size); + const spv_context context = spvContextCreate(random_gen.GetTargetEnv()); + if (context == nullptr) { + return 0; + } + std::vector input; input.resize(size >> 2); size_t count = 0; diff --git a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp index b45a98c3..6f4d7e8b 100644 --- a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); optimizer.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp index 6c3bd6ab..9c47d7d7 100644 --- a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); optimizer.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/test/fuzzers/spvtools_opt_size_fuzzer.cpp index 68c79747..10fac42a 100644 --- a/test/fuzzers/spvtools_opt_size_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_size_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); optimizer.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/test/fuzzers/spvtools_val_fuzzer.cpp b/test/fuzzers/spvtools_val_fuzzer.cpp index 5dc4303b..fd6396cd 100644 --- a/test/fuzzers/spvtools_val_fuzzer.cpp +++ b/test/fuzzers/spvtools_val_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::SpirvTools tools(random_gen.GetTargetEnv()); tools.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); -- cgit v1.2.3 From 63a39123262ef79e315ed84720d6ed519f0cc939 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Tue, 5 Oct 2021 12:55:06 -0600 Subject: Fix ConstantManager to not run AnalyzeInstDefUse if DefUse not valid (#4557) This fixes inlining which has to create constant for DebugInlinedAt for NonSemantic.Shader.DebugInfo. Also adds regression tests. --- source/opt/constants.cpp | 3 +- test/opt/inline_test.cpp | 173 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index a3dac5d7..020e248b 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp @@ -217,7 +217,8 @@ Instruction* ConstantManager::BuildInstructionAndAddToModule( auto* new_inst_ptr = new_inst.get(); *pos = pos->InsertBefore(std::move(new_inst)); ++(*pos); - context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); MapConstantToInst(new_const, new_inst_ptr); return new_inst_ptr; } diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index 29399013..dfc5f098 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -3259,6 +3259,82 @@ TEST_F(InlineTest, DebugSimple) { SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, ShaderDebugSimple) { + // Same as DebugSimple but for NonSemantic.Shader.DebugInfo.100. + const std::string text = R"( +; CHECK: [[main_name:%\d+]] = OpString "main" +; CHECK: [[foo_name:%\d+]] = OpString "foo" +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} %uint_4 %uint_1 {{%\d+}} [[main_name]] %uint_3 %uint_4 +; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} [[foo_name]] %uint_3 %uint_1 +; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} %uint_1 %uint_14 [[dbg_foo]] +; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt %uint_4 [[dbg_main]] +; CHECK: [[main:%\d+]] = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]] +; CHECK: [[foo:%\d+]] = OpFunction %v4float None + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %3 %4 + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %6 = OpString "float" + %main_name = OpString "main" + %foo_name = OpString "foo" + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_14 = OpConstant %uint 14 + %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 + %20 = OpExtInst %void %1 DebugSource %5 + %21 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %20 %uint_5 + %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 %uint_3 %uint_0 + %23 = OpExtInst %void %1 DebugTypeVector %22 %uint_4 + %24 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 %23 + %25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 + %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 %uint_4 %uint_1 %21 %main_name %uint_3 %uint_4 + %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 %uint_1 %uint_1 %21 %foo_name %uint_3 %uint_1 + %29 = OpExtInst %void %1 DebugLexicalBlock %20 %uint_1 %uint_14 %dbg_foo + %main = OpFunction %void None %18 + %30 = OpLabel +%dbg_main_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_main %main + %31 = OpExtInst %void %1 DebugScope %dbg_main + %32 = OpFunctionCall %v4float %foo + %33 = OpLoad %v4float %3 + %34 = OpFAdd %v4float %32 %33 + OpStore %4 %34 + OpReturn + OpFunctionEnd + %foo = OpFunction %v4float None %19 + %36 = OpLabel +%dbg_foo_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_foo %foo + %35 = OpExtInst %void %1 DebugScope %dbg_foo + %37 = OpExtInst %void %1 DebugScope %29 + OpReturnValue %14 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(InlineTest, DebugNested) { // When function main() calls function zoo() and function zoo() calls // function bar() and function bar() calls function foo(), check that @@ -3462,6 +3538,103 @@ float4 main(float4 color : COLOR) : SV_TARGET { SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, ShaderDebugSimpleHLSLPixelShader) { + // Same as DebugSimpleHLSLPixelShader but for + // NonSemantic.Shader.DebugInfo.100. + const std::string text = R"( +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1 +; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_1 %uint_47 [[dbg_main]] +; CHECK: %main = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]] +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_10 %uint_10 +; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_3 %uint_3 +; CHECK: OpFunctionEnd +; CHECK: %src_main = OpFunction %v4float None + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %14 = OpString "#line 1 \"ps.hlsl\" +float4 main(float4 color : COLOR) : SV_TARGET { + return color; +} +" + %17 = OpString "float" + %21 = OpString "src.main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %main "main" + OpName %param_var_color "param.var.color" + OpName %src_main "src.main" + OpName %color "color" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_10 = OpConstant %uint 10 + %uint_20 = OpConstant %uint 20 + %uint_32 = OpConstant %uint 32 + %uint_47 = OpConstant %uint 47 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %33 = OpTypeFunction %v4float %_ptr_Function_v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_3 %uint_0 + %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_4 + %20 = OpExtInst %void %1 DebugTypeFunction %uint_3 %19 %19 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_1 %uint_1 %16 %21 %uint_3 %uint_1 + %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_1 %uint_20 %22 %uint_4 %uint_0 + %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_1 %uint_47 %22 + %main = OpFunction %void None %27 + %28 = OpLabel +%param_var_color = OpVariable %_ptr_Function_v4float Function + %31 = OpLoad %v4float %in_var_COLOR + OpStore %param_var_color %31 + %32 = OpFunctionCall %v4float %src_main %param_var_color + OpStore %out_var_SV_TARGET %32 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %33 + %color = OpFunctionParameter %_ptr_Function_v4float + %bb_entry = OpLabel + %140 = OpExtInst %void %1 DebugFunctionDefinition %22 %src_main + %141 = OpExtInst %void %1 DebugLine %5 %uint_1 %uint_1 %uint_1 %uint_1 + %34 = OpExtInst %void %1 DebugScope %22 + %36 = OpExtInst %void %1 DebugDeclare %25 %color %13 + %38 = OpExtInst %void %1 DebugScope %26 + %142 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_10 %uint_10 + %39 = OpLoad %v4float %color + %143 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_3 %uint_3 + OpReturnValue %39 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(InlineTest, DebugDeclareForCalleeFunctionParam) { // Check that InlinePass correctly generates DebugDeclare instructions // for callee function's parameters and maps them to corresponding -- cgit v1.2.3 From 0c4deebc966b4e3dc29e0c8504959eff7b11f691 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 6 Oct 2021 15:50:12 +0100 Subject: Include a maximum value for spv_target_env (#4559) To allow querying the range of target environments (to ensure that a target environment value is within the valid range of the associated enum), this change adds a maximum value to the spv_target_env enumeration. --- include/spirv-tools/libspirv.h | 1 + source/spirv_target_env.cpp | 31 +++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 8a06db68..8df14f5f 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -516,6 +516,7 @@ typedef enum { SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions. SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. + SPV_ENV_MAX // Keep this as the last enum value. } spv_target_env; // SPIR-V Validator can be parameterized with the following Universal Limits. diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index 1ff1cbf1..187ab61e 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -62,7 +62,7 @@ const char* spvTargetEnvDescription(spv_target_env env) { case SPV_ENV_VULKAN_1_1: return "SPIR-V 1.3 (under Vulkan 1.1 semantics)"; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); break; case SPV_ENV_UNIVERSAL_1_4: return "SPIR-V 1.4"; @@ -72,6 +72,9 @@ const char* spvTargetEnvDescription(spv_target_env env) { return "SPIR-V 1.5"; case SPV_ENV_VULKAN_1_2: return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; } return ""; } @@ -102,7 +105,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1: return SPV_SPIRV_VERSION_WORD(1, 3); case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); break; case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: @@ -110,6 +113,9 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: return SPV_SPIRV_VERSION_WORD(1, 5); + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; } return SPV_SPIRV_VERSION_WORD(0, 0); } @@ -212,7 +218,10 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_2: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return false; @@ -246,7 +255,10 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_OPENCL_2_2: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return false; @@ -280,7 +292,10 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_OPENGL_4_5: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return false; @@ -313,6 +328,7 @@ bool spvIsValidEnv(spv_target_env env) { case SPV_ENV_OPENGL_4_5: return true; case SPV_ENV_WEBGPU_0: + case SPV_ENV_MAX: break; } return false; @@ -352,7 +368,10 @@ std::string spvLogStringForEnv(spv_target_env env) { return "Universal"; } case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return "Unknown"; -- cgit v1.2.3 From b46995741b97c714e211fe5df8590991ae998475 Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 6 Oct 2021 16:50:16 -0400 Subject: Avoid bugprone-move-forwarding-reference warning in Clang (#4560) Use std::forward instead of std::move, on an argument with rvalue-reference of template-deduced type. See https://clang.llvm.org/extra/clang-tidy/checks/bugprone-move-forwarding-reference.html Bug: crbug.com/1134310 --- source/opt/iterator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/opt/iterator.h b/source/opt/iterator.h index 2280582d..847e1bbd 100644 --- a/source/opt/iterator.h +++ b/source/opt/iterator.h @@ -142,7 +142,7 @@ inline IteratorRange make_range(const IteratorType& begin, template inline IteratorRange make_range(IteratorType&& begin, IteratorType&& end) { - return {std::move(begin), std::move(end)}; + return {std::forward(begin), std::forward(end)}; } // Returns a (begin, end) iterator pair for the given container. -- cgit v1.2.3 From 6c6f1fe1fbe7b072287d374eb22beaedf7660df8 Mon Sep 17 00:00:00 2001 From: Paul Thomson Date: Tue, 12 Oct 2021 14:14:17 +0100 Subject: Update Android.bp Bug: b/201652781 Change-Id: Ia168f80ffcc41d8d79aeb1c0c6bdd8592d196996 --- Android.bp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Android.bp b/Android.bp index 07bd32b1..8aeedaa7 100644 --- a/Android.bp +++ b/Android.bp @@ -39,6 +39,7 @@ genrule { "extension_enum.inc", "glsl.std.450.insts.inc", "nonsemantic.clspvreflection.insts.inc", + "nonsemantic.vulkan.debuginfo.100.insts.inc", "opencl.debuginfo.100.insts.inc", "opencl.std.insts.inc", "operand.kinds-unified1.inc", @@ -58,6 +59,7 @@ genrule { ":deqp_spirv_headers_unified1_extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json", ":deqp_spirv_headers_unified1_extinst.spv-amd-shader-trinary-minmax.grammar.json", ":deqp_spirv_headers_unified1_spirv.core.grammar.json", + "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json", ], tool_files: ["utils/generate_grammar_tables.py"], cmd: @@ -67,6 +69,7 @@ genrule { "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --vendor-insts-output=$(location debuginfo.insts.inc) --vendor-operand-kind-prefix=; "+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-ballot.grammar.json) --vendor-insts-output=$(location spv-amd-shader-ballot.insts.inc) --vendor-operand-kind-prefix=; "+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --vendor-insts-output=$(location opencl.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=CLDEBUG100_; "+ + "$(location) --extinst-vendor-grammar=$(location source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json) --vendor-insts-output=$(location nonsemantic.vulkan.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=VKDEBUG100_; "+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.nonsemantic.clspvreflection.grammar.json) --vendor-insts-output=$(location nonsemantic.clspvreflection.insts.inc) --vendor-operand-kind-prefix=; "+ "$(location) --extinst-opencl-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.std.100.grammar.json) --opencl-insts-output=$(location opencl.std.insts.inc); "+ "$(location) --extinst-glsl-grammar=$(location :deqp_spirv_headers_unified1_extinst.glsl.std.450.grammar.json) --glsl-insts-output=$(location glsl.std.450.insts.inc); "+ @@ -80,15 +83,18 @@ genrule { out: [ "DebugInfo.h", "OpenCLDebugInfo100.h", + "NonSemanticVulkanDebugInfo100.h", ], srcs: [ ":deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json", ":deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json", + "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json", ], tool_files: ["utils/generate_language_headers.py"], cmd: "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --extinst-output-path=$(location DebugInfo.h); "+ - "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --extinst-output-path=$(location OpenCLDebugInfo100.h); " + "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --extinst-output-path=$(location OpenCLDebugInfo100.h); "+ + "$(location) --extinst-grammar=$(location source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json) --extinst-output-path=$(location NonSemanticVulkanDebugInfo100.h); " } genrule { -- cgit v1.2.3 From e4349dd8f45ecb7ce8cf9b8edf6644a55e849d17 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Wed, 13 Oct 2021 12:16:54 -0400 Subject: Fix CI failure (#4570) --- source/val/validate_image.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index e5968d06..d96856c8 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -66,6 +66,11 @@ bool CheckAllImageOperandsHandled() { case SpvImageOperandsVolatileTexelKHRMask: case SpvImageOperandsSignExtendMask: case SpvImageOperandsZeroExtendMask: + // TODO(jaebaek): Move this line properly after handling image offsets + // operand. This line temporarily fixes CI failure that + // blocks other PRs. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565 + case SpvImageOperandsOffsetsMask: return true; } return false; -- cgit v1.2.3 From 6dd73728e932c8c9400493795c37bec8adc32dbc Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Wed, 13 Oct 2021 11:42:40 -0600 Subject: Fix merge-block assertions with debugInfo (#4563) Fixes DefUse assertions and invalid DebugScope instruction between OpLoopMerge and OpBranch for included test shader. --- source/opt/block_merge_util.cpp | 17 ++++--- test/opt/block_merge_test.cpp | 106 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp index e4ba10fc..8ae8020a 100644 --- a/source/opt/block_merge_util.cpp +++ b/source/opt/block_merge_util.cpp @@ -171,12 +171,17 @@ void MergeWithSuccessor(IRContext* context, Function* func, // and OpBranchConditional. auto terminator = bi->terminator(); auto& vec = terminator->dbg_line_insts(); - auto& new_vec = merge_inst->dbg_line_insts(); - new_vec.insert(new_vec.end(), vec.begin(), vec.end()); - terminator->ClearDbgLineInsts(); - for (auto& l_inst : new_vec) - context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); - + if (vec.size() > 0) { + merge_inst->ClearDbgLineInsts(); + auto& new_vec = merge_inst->dbg_line_insts(); + new_vec.insert(new_vec.end(), vec.begin(), vec.end()); + terminator->ClearDbgLineInsts(); + for (auto& l_inst : new_vec) + context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); + } + // Clear debug scope of terminator to avoid DebugScope + // emitted between terminator and merge. + terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt)); // Move the merge instruction to just before the terminator. merge_inst->InsertBefore(terminator); } diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp index 140a5c09..b9d2db3a 100644 --- a/test/opt/block_merge_test.cpp +++ b/test/opt/block_merge_test.cpp @@ -1038,6 +1038,112 @@ OpFunctionEnd SinglePassRunAndCheck(spirv, spirv, true, true); } +TEST_F(BlockMergeTest, DebugMerge) { + // Verify merge can be done completely, cleanly and validly in presence of + // NonSemantic.Shader.DebugInfo.100 instructions + const std::string text = R"( +; CHECK: OpLoopMerge +; CHECK-NEXT: OpBranch +; CHECK-NOT: OpBranch +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%5 = OpString "lexblock.hlsl" +%20 = OpString "float" +%32 = OpString "main" +%33 = OpString "" +%46 = OpString "b" +%49 = OpString "a" +%58 = OpString "c" +%63 = OpString "color" +OpName %in_var_COLOR "in.var.COLOR" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %main "main" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%9 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%13 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_3 = OpConstant %uint 3 +%uint_0 = OpConstant %uint 0 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_12 = OpConstant %uint 12 +%uint_13 = OpConstant %uint 13 +%uint_20 = OpConstant %uint 20 +%uint_15 = OpConstant %uint 15 +%uint_17 = OpConstant %uint 17 +%uint_16 = OpConstant %uint 16 +%uint_14 = OpConstant %uint 14 +%uint_10 = OpConstant %uint 10 +%65 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%62 = OpExtInst %void %1 DebugExpression +%22 = OpExtInst %void %1 DebugTypeBasic %20 %uint_32 %uint_3 %uint_0 +%25 = OpExtInst %void %1 DebugTypeVector %22 %uint_4 +%27 = OpExtInst %void %1 DebugTypeFunction %uint_3 %25 %25 +%28 = OpExtInst %void %1 DebugSource %5 +%29 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %28 %uint_5 +%34 = OpExtInst %void %1 DebugFunction %32 %27 %28 %uint_12 %uint_1 %29 %33 %uint_3 %uint_13 +%37 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_13 %uint_1 %34 +%52 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_15 %uint_12 %37 +%54 = OpExtInst %void %1 DebugLocalVariable %46 %25 %28 %uint_17 %uint_12 %52 %uint_4 +%56 = OpExtInst %void %1 DebugLocalVariable %49 %25 %28 %uint_16 %uint_12 %52 %uint_4 +%59 = OpExtInst %void %1 DebugLocalVariable %58 %25 %28 %uint_14 %uint_10 %37 %uint_4 +%64 = OpExtInst %void %1 DebugLocalVariable %63 %25 %28 %uint_12 %uint_20 %34 %uint_4 %uint_1 +%main = OpFunction %void None %65 +%66 = OpLabel +%69 = OpLoad %v4float %in_var_COLOR +%168 = OpExtInst %void %1 DebugValue %64 %69 %62 +%169 = OpExtInst %void %1 DebugScope %37 +OpLine %5 14 10 +%164 = OpExtInst %void %1 DebugValue %59 %9 %62 +OpLine %5 15 3 +OpBranch %150 +%150 = OpLabel +%165 = OpPhi %v4float %9 %66 %158 %159 +%167 = OpExtInst %void %1 DebugValue %59 %165 %62 +%170 = OpExtInst %void %1 DebugScope %37 +OpLine %5 15 12 +%171 = OpExtInst %void %1 DebugNoScope +OpLoopMerge %160 %159 None +OpBranch %151 +%151 = OpLabel +OpLine %5 16 12 +%162 = OpExtInst %void %1 DebugValue %56 %9 %62 +OpLine %5 17 12 +%163 = OpExtInst %void %1 DebugValue %54 %13 %62 +OpLine %5 18 15 +%158 = OpFAdd %v4float %165 %13 +OpLine %5 18 5 +%166 = OpExtInst %void %1 DebugValue %59 %158 %62 +%172 = OpExtInst %void %1 DebugScope %37 +OpLine %5 19 3 +OpBranch %159 +%159 = OpLabel +OpLine %5 19 3 +OpBranch %150 +%160 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow -- cgit v1.2.3 From 0292d6b16abca5a1c868da2789b6ff307b563d3f Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 13 Oct 2021 17:50:17 -0400 Subject: Update SPIRV-Headers (#4573) * Add pass-through validation for Offsets image operand * it is counted as an offset parameter though * Update DEPS --- DEPS | 2 +- source/val/validate_image.cpp | 15 ++++++++++----- test/val/val_image_test.cpp | 14 ++++++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/DEPS b/DEPS index 5212deae..bf768939 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', 'googletest_revision': '955c7f837efad184ec63e771c42542d37545eaef', 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', - 'spirv_headers_revision': 'ae217c17809fadb232ec94b29304b4afcd417bb4', + 'spirv_headers_revision': '19e8350415ed9516c8afffa19ae2c58559495a67', } deps = { diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index d96856c8..3baff820 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -286,13 +286,14 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // the module to be invalid. if (mask == 0) return SPV_SUCCESS; - if (spvtools::utils::CountSetBits( - mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask | - SpvImageOperandsConstOffsetsMask)) > 1) { + if (spvtools::utils::CountSetBits(mask & (SpvImageOperandsOffsetMask | + SpvImageOperandsConstOffsetMask | + SpvImageOperandsConstOffsetsMask | + SpvImageOperandsOffsetsMask)) > 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4662) - << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used " - << "together"; + << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together"; } const bool is_implicit_lod = IsImplicitLod(opcode); @@ -625,6 +626,10 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // setup. } + if (mask & SpvImageOperandsOffsetsMask) { + // TODO: add validation + } + return SPV_SUCCESS; } diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 701e35e1..f49c81a5 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -1815,9 +1815,10 @@ TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " - "cannot be used together")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together")); } TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) { @@ -1833,9 +1834,10 @@ TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-Offset-04662")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " - "cannot be used together")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together")); } TEST_F(ValidateImage, SampleImplicitLodMinLodWrongType) { -- cgit v1.2.3 From d31218c560075342dac35aaa3d26b188ff0c87fa Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Wed, 13 Oct 2021 17:53:11 -0400 Subject: Use max enum instead of static array of legal values (#4571) --- test/fuzzers/random_generator.cpp | 26 +++++++++----------------- test/fuzzers/random_generator.h | 2 +- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/test/fuzzers/random_generator.cpp b/test/fuzzers/random_generator.cpp index 0e932716..878961fc 100644 --- a/test/fuzzers/random_generator.cpp +++ b/test/fuzzers/random_generator.cpp @@ -87,23 +87,15 @@ RandomGenerator::RandomGenerator(const uint8_t* data, size_t size) { } spv_target_env RandomGenerator::GetTargetEnv() { - // SPV_ENV_WEBGPU_0 is intentionally omitted here, since it is deprecated and - // using it will cause asserts. - static const std::array envs = { - SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_0, - SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, - SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, - SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_EMBEDDED_1_2, - SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_EMBEDDED_2_0, - SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_EMBEDDED_2_2, - SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2, - SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, - SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_2, - SPV_ENV_OPENGL_4_0, SPV_ENV_OPENGL_4_1, - SPV_ENV_OPENGL_4_2, SPV_ENV_OPENGL_4_3, - SPV_ENV_OPENGL_4_5}; - - return envs[RandomUInt(&engine_, 0lu, envs.size())]; + spv_target_env result; + + // Need to check that the generated value isn't for a deprecated target env. + do { + result = static_cast( + RandomUInt(&engine_, 0u, static_cast(SPV_ENV_MAX))); + } while (!spvIsValidEnv(result)); + + return result; } uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) { diff --git a/test/fuzzers/random_generator.h b/test/fuzzers/random_generator.h index b9fc2759..61710556 100644 --- a/test/fuzzers/random_generator.h +++ b/test/fuzzers/random_generator.h @@ -44,7 +44,7 @@ class RandomGenerator { /// @param size - number of elements in |data|, must be > 0 static uint64_t CalculateSeed(const uint8_t* data, size_t size); - /// Get random target env. + /// Get random valid target env. spv_target_env GetTargetEnv(); private: -- cgit v1.2.3 From 5ed637caa3a120e151712cd4c2f2f31bf1ee7211 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 14 Oct 2021 14:13:47 +0100 Subject: Fix output of SPIR-V version in diagnostic (#4562) Fixes #4552. --- source/val/validate_instruction.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp index 9d395fb4..dad98673 100644 --- a/source/val/validate_instruction.cpp +++ b/source/val/validate_instruction.cpp @@ -318,10 +318,9 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { if (module_version < min_version) { return _.diag(SPV_ERROR_WRONG_VERSION, inst) - << spvOpcodeString(opcode) << " requires " - << spvTargetEnvDescription( - static_cast(min_version)) - << " at minimum."; + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum."; } } else if (!_.HasAnyOfExtensions(exts)) { // Otherwise, we only error out when no enabling extensions are -- cgit v1.2.3 From e6e77dbdfa799b3be2faa12f4dd8a16f6e3b4cc7 Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 14 Oct 2021 12:21:11 -0400 Subject: Enable OpConstFunctionPointerINTEL outside function (#4576) According to spec this opcode is a constant instruction - that's it can appear outside of function bodies. Co-authored-by: DmitryBushev --- source/opt/reflect.h | 3 ++- test/opt/ir_loader_test.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/source/opt/reflect.h b/source/opt/reflect.h index d300fe5d..ffd2805a 100644 --- a/source/opt/reflect.h +++ b/source/opt/reflect.h @@ -51,7 +51,8 @@ inline bool IsTypeInst(SpvOp opcode) { opcode == SpvOpTypeCooperativeMatrixNV; } inline bool IsConstantInst(SpvOp opcode) { - return opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp; + return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) || + opcode == SpvOpConstFunctionPointerINTEL; } inline bool IsCompileTimeConstantInst(SpvOp opcode) { return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull; diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp index 475dd235..6eafcd9d 100644 --- a/test/opt/ir_loader_test.cpp +++ b/test/opt/ir_loader_test.cpp @@ -105,6 +105,22 @@ TEST(IrBuilder, RoundTripIncompleteFunction) { DoRoundTripCheck("%2 = OpFunction %1 None %3\n"); } +TEST(IrBuilder, RoundTripFunctionPointer) { + DoRoundTripCheck( + "OpCapability Linkage\n" + "OpCapability FunctionPointersINTEL\n" + "OpName %some_function \"some_function\"\n" + "OpName %ptr_to_function \"ptr_to_function\"\n" + "OpDecorate %some_function LinkageAttributes \"some_function\" Import\n" + "%float = OpTypeFloat 32\n" + "%4 = OpTypeFunction %float %float\n" + "%_ptr_Function_4 = OpTypePointer Function %4\n" + "%ptr_to_function = OpConstFunctionPointerINTEL %_ptr_Function_4 " + "%some_function\n" + "%some_function = OpFunction %float Const %4\n" + "%6 = OpFunctionParameter %float\n" + "OpFunctionEnd\n"); +} TEST(IrBuilder, KeepLineDebugInfo) { // #version 310 es // void main() {} -- cgit v1.2.3 From 3e6a85303dfb72d8768da651da946913bac6f05e Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Thu, 14 Oct 2021 11:29:54 -0600 Subject: Mark DebugInfoNone as live in ADCE when DebugInfo present (#4568) Otherwise KillInst() tries to generate it when the module is inconsistent. --- source/opt/aggressive_dead_code_elim_pass.cpp | 11 ++- test/opt/aggressive_dead_code_elim_test.cpp | 98 +++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 1f922536..2eac075d 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -636,16 +636,25 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // For each DebugInfo GlobalVariable keep all operands except the Variable. - // Later, if the variable is dead, we will set the operand to DebugInfoNone. + // Later, if the variable is killed with KillInst(), we will set the operand + // to DebugInfoNone. Create and save DebugInfoNone now for this possible + // later use. This is slightly unoptimal, but it avoids generating it during + // instruction killing when the module is not consistent. + bool debug_global_seen = false; for (auto& dbg : get_module()->ext_inst_debuginfo()) { if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; + debug_global_seen = true; dbg.ForEachInId([this](const uint32_t* iid) { Instruction* in_inst = get_def_use_mgr()->GetDef(*iid); if (in_inst->opcode() == SpvOpVariable) return; AddToWorklist(in_inst); }); } + if (debug_global_seen) { + auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone(); + AddToWorklist(dbg_none); + } } Pass::Status AggressiveDCEPass::ProcessImpl() { diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 0cff2681..746569ab 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -7246,6 +7246,104 @@ TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) { SinglePassRunAndMatch(text, true); } +TEST_F(AggressiveDCETest, ShaderDebugInfoGlobalDCE) { + // Verify that DebugGlobalVariable for eliminated private variable has + // variable operand replaced with DebugInfoNone. + + const std::string text = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %out_var_SV_Target0 %a +OpExecutionMode %MainPs OriginUpperLeft +%5 = OpString "source2.hlsl" +%24 = OpString "float" +%29 = OpString "vColor" +%33 = OpString "PS_OUTPUT" +%37 = OpString "MainPs" +%38 = OpString "" +%42 = OpString "ps_output" +%46 = OpString "a" +OpName %a "a" +OpName %out_var_SV_Target0 "out.var.SV_Target0" +OpName %MainPs "MainPs" +OpName %PS_OUTPUT "PS_OUTPUT" +OpMemberName %PS_OUTPUT 0 "vColor" +OpDecorate %out_var_SV_Target0 Location 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%8 = OpConstantNull %v4float +%float_0 = OpConstant %float 0 +%10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_1 = OpConstant %uint 1 +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%uint_0 = OpConstant %uint 0 +%uint_128 = OpConstant %uint 128 +%uint_12 = OpConstant %uint 12 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_10 = OpConstant %uint 10 +%uint_15 = OpConstant %uint 15 +%48 = OpTypeFunction %void +%PS_OUTPUT = OpTypeStruct %v4float +%54 = OpTypeFunction %PS_OUTPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v4float = OpTypePointer Function %v4float +%a = OpVariable %_ptr_Private_v4float Private +;CHECK-NOT: %a = OpVariable %_ptr_Private_v4float Private +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output +;CHECK: [[dbg_none:%\w+]] = OpExtInst %void %1 DebugInfoNone +%18 = OpExtInst %void %1 DebugExpression +%19 = OpExtInst %void %1 DebugSource %5 +%20 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %19 %uint_5 +%25 = OpExtInst %void %1 DebugTypeBasic %24 %uint_32 %uint_3 %uint_0 +%28 = OpExtInst %void %1 DebugTypeVector %25 %uint_4 +%31 = OpExtInst %void %1 DebugTypeMember %29 %28 %19 %uint_5 %uint_12 %uint_0 %uint_128 %uint_3 +%34 = OpExtInst %void %1 DebugTypeComposite %33 %uint_1 %19 %uint_3 %uint_8 %20 %33 %uint_128 %uint_3 %31 +%36 = OpExtInst %void %1 DebugTypeFunction %uint_3 %34 +%39 = OpExtInst %void %1 DebugFunction %37 %36 %19 %uint_8 %uint_1 %20 %38 %uint_3 %uint_9 +%41 = OpExtInst %void %1 DebugLexicalBlock %19 %uint_9 %uint_1 %39 +%43 = OpExtInst %void %1 DebugLocalVariable %42 %34 %19 %uint_10 %uint_15 %41 %uint_4 +%47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 %a %uint_8 +;CHECK: %47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 [[dbg_none]] %uint_8 +%MainPs = OpFunction %void None %48 +%49 = OpLabel +%65 = OpVariable %_ptr_Function_PS_OUTPUT Function +%66 = OpVariable %_ptr_Function_PS_OUTPUT Function +OpStore %a %8 +%72 = OpExtInst %void %1 DebugScope %41 +%69 = OpExtInst %void %1 DebugDeclare %43 %65 %18 +OpLine %5 11 5 +%70 = OpAccessChain %_ptr_Function_v4float %65 %int_0 +OpStore %70 %10 +OpLine %5 12 12 +%71 = OpLoad %PS_OUTPUT %65 +OpLine %5 12 5 +OpStore %66 %71 +%73 = OpExtInst %void %1 DebugNoLine +%74 = OpExtInst %void %1 DebugNoScope +%51 = OpLoad %PS_OUTPUT %66 +%53 = OpCompositeExtract %v4float %51 0 +OpStore %out_var_SV_Target0 %53 +OpLine %5 13 1 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) { // Verify that local variable tc and its store are kept by DebugDeclare. // -- cgit v1.2.3 From 10343e53edf27e631bcb9df6d089ae2803ed5268 Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 14 Oct 2021 17:46:27 -0400 Subject: Remove Appveyor CI, update README (#4578) - Remove Appveyor CI - Write new "Getting the source" subsection, consolidating material from a couple of places. Emphasize using the utils/git-sync-deps script for getting a known-good combination of sources. - Built artifacts: - top of tree artifacts are from Kokoro CI bots - mention Vulkan SDK and Android NDK as alternatives Fixes: #4577 --- .appveyor.yml | 90 ------------------------------------------------------- README.md | 84 ++++++++++++++++++++++----------------------------- docs/downloads.md | 20 +++++++++++-- 3 files changed, 52 insertions(+), 142 deletions(-) delete mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 0a4cca05..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,90 +0,0 @@ -# Windows Build Configuration for AppVeyor -# http://www.appveyor.com/docs/appveyor-yml - -# version format -version: "{build}" - -# The most recent compiler gives the most interesting new results. -# Put it first so we get its feedback first. -os: - - Visual Studio 2017 - #- Visual Studio 2013 - -platform: - - x64 - -configuration: - - Debug - #- Release - -branches: - only: - - master - -# Travis advances the master-tot tag to current top of the tree after -# each push into the master branch, because it relies on that tag to -# upload build artifacts to the master-tot release. This will cause -# double testing for each push on Appveyor: one for the push, one for -# the tag advance. Disable testing tags. -skip_tags: true - -clone_depth: 1 - -matrix: - fast_finish: true # Show final status immediately if a test fails. - #exclude: - # - os: Visual Studio 2013 - # configuration: Debug - -# scripts that run after cloning repository -install: - # Install ninja - - set NINJA_URL="https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-win.zip" - - appveyor DownloadFile %NINJA_URL% -FileName ninja.zip - - 7z x ninja.zip -oC:\ninja > nul - - set PATH=C:\ninja;C:\Python36;%PATH% - -before_build: - - git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers - - git clone https://github.com/google/googletest.git external/googletest - - cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. - - git clone --depth=1 https://github.com/google/effcee.git external/effcee - - git clone --depth=1 https://github.com/google/re2.git external/re2 - # Set path and environment variables for the current Visual Studio version - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64) - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64) - -build: - parallel: true # enable MSBuild parallel builds - verbosity: minimal - -build_script: - - mkdir build && cd build - - cmake -GNinja -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_INSTALL_PREFIX=install -DRE2_BUILD_TESTING=OFF .. - - ninja install - -test_script: - - ctest -C %CONFIGURATION% --output-on-failure --timeout 310 - -after_test: - # Zip build artifacts for uploading and deploying - - cd install - - 7z a SPIRV-Tools-master-windows-"%PLATFORM%"-"%CONFIGURATION%".zip *\* - -artifacts: - - path: build\install\*.zip - name: artifacts-zip - -deploy: - - provider: GitHub - auth_token: - secure: TMfcScKzzFIm1YgeV/PwCRXFDCw8Xm0wY2Vb2FU6WKlbzb5eUITTpr6I5vHPnAxS - release: master-tot - description: "Continuous build of the latest master branch by Appveyor and Travis CI" - artifact: artifacts-zip - draft: false - prerelease: false - force_update: true - on: - branch: master - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 diff --git a/README.md b/README.md index 9230fd1a..1e3f7745 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ These versions undergo extra testing. Releases are not directly related to releases (or versions) of [SPIRV-Headers][spirv-headers]. Releases of SPIRV-Tools are tested against the version of SPIRV-Headers listed -in the DEPS file. +in the [DEPS](DEPS) file. The release generally uses the most recent compatible version of SPIRV-Headers available at the time of release. No version of SPIRV-Headers other than the one listed in the DEPS file is @@ -256,6 +256,34 @@ Contributions via merge request are welcome. Changes should: We intend to maintain a linear history on the GitHub `master` branch. +### Getting the source + +Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: + + git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools + cd spirv-tools + + # Check out sources for dependencies, at versions known to work together, + # as listed in the DEPS file. + python3 utils/git-sync-deps + +For some kinds of development, you may need the latest sources from the third-party projects: + + git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers + git clone https://github.com/google/googletest.git spirv-tools/external/googletest + git clone https://github.com/google/effcee.git spirv-tools/external/effcee + git clone https://github.com/google/re2.git spirv-tools/external/re2 + +#### Dependency on Effcee + +Some tests depend on the [Effcee][effcee] library for stateful matching. +Effcee itself depends on [RE2][re2]. + +* If SPIRV-Tools is configured as part of a larger project that already uses + Effcee, then that project should include Effcee before SPIRV-Tools. +* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` + and RE2 sources to appear in `external/re2`. + ### Source code organization * `example`: demo code of using SPIRV-Tools APIs @@ -274,14 +302,6 @@ We intend to maintain a linear history on the GitHub `master` branch. * `test/`: Tests, using the [googletest][googletest] framework * `tools/`: Command line executables -Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: - - git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools - git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers - git clone https://github.com/google/googletest.git spirv-tools/external/googletest - git clone https://github.com/google/effcee.git spirv-tools/external/effcee - git clone https://github.com/google/re2.git spirv-tools/external/re2 - ### Tests The project contains a number of tests, used to drive development @@ -295,46 +315,12 @@ tests: `googletest` source into the `/external/googletest` directory before configuring and building the project. -*Note*: You must use a version of googletest that includes -[a fix][googletest-pull-612] for [googletest issue 610][googletest-issue-610]. -The fix is included on the googletest master branch any time after 2015-11-10. -In particular, googletest must be newer than version 1.7.0. - -### Dependency on Effcee - -Some tests depend on the [Effcee][effcee] library for stateful matching. -Effcee itself depends on [RE2][re2]. - -* If SPIRV-Tools is configured as part of a larger project that already uses - Effcee, then that project should include Effcee before SPIRV-Tools. -* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` - and RE2 sources to appear in `external/re2`. - - ## Build -Instead of building manually, you can also download the binaries for your -platform directly from the [master-tot release][master-tot-release] on GitHub. -Those binaries are automatically uploaded by the buildbots after successful -testing and they always reflect the current top of the tree of the master -branch. +*Note*: Prebuilt binaries are available from the [downloads](docs/downloads.md) page. -In order to build the code, you first need to sync the external repositories -that it depends on. Assume that `` is the root directory of the -checked out code: - -```sh -cd -git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers -git clone https://github.com/google/effcee.git external/effcee -git clone https://github.com/google/re2.git external/re2 -git clone https://github.com/google/googletest.git external/googletest # optional - -``` - -*Note*: -The script `utils/git-sync-deps` can be used to checkout and/or update the -contents of the repos under `external/` instead of manually maintaining them. +First [get the sources](#getting-the-source). +Then build using CMake, Bazel, or Android ndk-build. ### Build using CMake You can build the project using [CMake][cmake]: @@ -435,7 +421,7 @@ via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to `/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and iterator debugging. -### Android +### Android ndk-build SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and `libSPIRV-Tools-opt.a` for Android: @@ -456,7 +442,8 @@ $ANDROID_NDK/ndk-build -C ../android_test \ ``` ### Updating DEPS -Occasionally the entries in DEPS will need to be updated. This is done on demand + +Occasionally the entries in [DEPS](DEPS) will need to be updated. This is done on demand when there is a request to do this, often due to downstream breakages. There is a script `utils/roll_deps.sh` provided, which will generate a patch with the updated DEPS values. This will still need to be tested in your checkout to @@ -743,4 +730,3 @@ limitations under the License. [CMake]: https://cmake.org/ [cpp-style-guide]: https://google.github.io/styleguide/cppguide.html [clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation -[master-tot-release]: https://github.com/KhronosGroup/SPIRV-Tools/releases/tag/master-tot diff --git a/docs/downloads.md b/docs/downloads.md index 9c7d8567..168937a7 100644 --- a/docs/downloads.md +++ b/docs/downloads.md @@ -1,14 +1,28 @@ # Downloads -Download the latest builds. -## Release +## Latest builds + +Download the latest builds of the [master](https://github.com/KhronosGroup/SPIRV-Tools/tree/master) branch. + +### Release build | Windows | Linux | MacOS | | --- | --- | --- | | [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) | | | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html) | | -## Debug +### Debug build | Windows | Linux | MacOS | | --- | --- | --- | | [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) | | | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | | + + +## Vulkan SDK + +SPIRV-Tools is published as part of the [LunarG Vulkan SDK](https://www.lunarg.com/vulkan-sdk/). +The Vulkan SDK is updated approximately every six weeks. + +## Android NDK + +SPIRV-Tools host executables, and library sources are published as +part of the [Android NDK](https://developer.android.com/ndk/downloads). -- cgit v1.2.3 From 06ebc4806b7de62b01b27f2faacd55d639a20180 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 14 Oct 2021 17:47:05 -0400 Subject: Account for strided components in arrays (#4575) * Account for strided components in arrays Fixes #4567 * If the element type of an array takes less than a location in size, calculate the location usage in a strided manner * formatting --- source/val/validate_interfaces.cpp | 45 ++++++++++++++++++++------- test/val/val_interfaces_test.cpp | 62 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index d3ef5386..7ccb6371 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -199,6 +199,10 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) { NumConsumedComponents(_, _.FindDef(type->GetOperandAs(1))); num_components *= type->GetOperandAs(2); break; + case SpvOpTypeArray: + // Skip the array. + return NumConsumedComponents(_, + _.FindDef(type->GetOperandAs(1))); default: // This is an error that is validated elsewhere. break; @@ -430,17 +434,36 @@ spv_result_t GetLocationsForVariable( continue; } - uint32_t end = (location + num_locations) * 4; - if (num_components != 0) { - start += component; - end = location * 4 + component + num_components; - } - for (uint32_t l = start; l < end; ++l) { - if (!locations->insert(l).second) { - return _.diag(SPV_ERROR_INVALID_DATA, entry_point) - << "Entry-point has conflicting " << storage_class - << " location assignment at location " << l / 4 - << ", component " << l % 4; + if (member->opcode() == SpvOpTypeArray && num_components >= 1 && + num_components < 4) { + // When an array has an element that takes less than a location in + // size, calculate the used locations in a strided manner. + for (uint32_t l = location; l < num_locations + location; ++l) { + for (uint32_t c = component; c < component + num_components; ++c) { + uint32_t check = 4 * l + c; + if (!locations->insert(check).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << l + << ", component " << c; + } + } + } + } else { + // TODO: There is a hole here is the member is an array of 3- or + // 4-element vectors of 64-bit types. + uint32_t end = (location + num_locations) * 4; + if (num_components != 0) { + start += component; + end = location * 4 + component + num_components; + } + for (uint32_t l = start; l < end; ++l) { + if (!locations->insert(l).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << l / 4 + << ", component " << l % 4; + } } } } diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index be13cd71..5ae19fa3 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -1410,6 +1410,68 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateInterfacesTest, VulkanLocationArrayWithComponent1) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 1 Location 0 +OpMemberDecorate %struct 1 Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%float_arr = OpTypeArray %float %int_2 +%struct = OpTypeStruct %float_arr %float_arr +%ptr = OpTypePointer Input %struct +%in = OpVariable %ptr Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationArrayWithComponent2) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 1 Location 0 +OpMemberDecorate %struct 1 Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%double_arr = OpTypeArray %double %int_2 +%struct = OpTypeStruct %float %double_arr +%ptr = OpTypePointer Input %struct +%in = OpVariable %ptr Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateInterfacesTest, DuplicateInterfaceVariableSuccess) { const std::string text = R"( OpCapability Shader -- cgit v1.2.3 From 35fd0e17d081b28a0ff15b376329205c20c7d1f8 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Fri, 15 Oct 2021 10:23:15 -0400 Subject: Consider 0xffffffff offset as missing (#4564) Fixes #4561 * When checking for offsets, don't accept 0xffffffff --- source/val/validate_decorations.cpp | 2 ++ test/val/val_decoration_test.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 8e4c42e4..b7e38a3f 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -140,6 +140,8 @@ bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) { for (auto& decoration : vstate.id_decorations(struct_id)) { if (SpvDecorationOffset == decoration.dec_type() && Decoration::kInvalidMember != decoration.struct_member_index()) { + // Offset 0xffffffff is not valid so ignore it for simplicity's sake. + if (decoration.params()[0] == 0xffffffff) return true; hasOffset[decoration.struct_member_index()] = true; } } diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index 9fdeb38f..508acfa1 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -7825,6 +7825,36 @@ OpFunctionEnd "laid out with Offset decorations")); } +TEST_F(ValidateDecorations, AllOnesOffset) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %outer Block +OpMemberDecorate %outer 0 Offset 0 +OpMemberDecorate %struct 0 Offset 4294967295 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%outer = OpTypeStruct %struct +%ptr = OpTypePointer Uniform %outer +%var = OpVariable %ptr Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("decorated as Block must be explicitly laid out with " + "Offset decorations")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 387cae472ef37412b6f60f4a722bccf1174a5acb Mon Sep 17 00:00:00 2001 From: JiaoluAMD Date: Tue, 19 Oct 2021 01:18:16 +0800 Subject: Opt passes should apply to the exported functions (#4554) This is follow-up to the commit https://github.com/KhronosGroup/SPIRV-Tools/commit/bd3a271ce3fcc8b9ed8e6eb2e94392d1b220adc9 --- source/opt/block_merge_pass.cpp | 2 +- source/opt/convert_to_half_pass.cpp | 2 +- source/opt/dead_insert_elim_pass.cpp | 2 +- source/opt/inline_exhaustive_pass.cpp | 2 +- source/opt/inline_opaque_pass.cpp | 2 +- source/opt/local_single_block_elim_pass.cpp | 2 +- source/opt/local_single_store_elim_pass.cpp | 2 +- source/opt/relax_float_ops_pass.cpp | 2 +- test/opt/block_merge_test.cpp | 57 +++++++++++++ test/opt/convert_relaxed_to_half_test.cpp | 92 ++++++++++++++++++++ test/opt/dead_insert_elim_test.cpp | 66 +++++++++++++++ test/opt/inline_opaque_test.cpp | 109 ++++++++++++++++++++++++ test/opt/inline_test.cpp | 126 ++++++++++++++++++++++++++++ test/opt/local_single_block_elim.cpp | 50 +++++++++++ test/opt/local_single_store_elim_test.cpp | 85 +++++++++++++++++++ test/opt/relax_float_ops_test.cpp | 80 ++++++++++++++++++ 16 files changed, 673 insertions(+), 8 deletions(-) diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp index 04e47f1c..ef7f31fe 100644 --- a/source/opt/block_merge_pass.cpp +++ b/source/opt/block_merge_pass.cpp @@ -44,7 +44,7 @@ bool BlockMergePass::MergeBlocks(Function* func) { Pass::Status BlockMergePass::Process() { // Process all entry point functions. ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index 6b3b540a..0b1afd2a 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp @@ -340,7 +340,7 @@ Pass::Status ConvertToHalfPass::ProcessImpl() { Pass::ProcessFunction pfn = [this](Function* fp) { return ProcessFunction(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); // If modified, make sure module has Float16 capability if (modified) context()->AddCapability(SpvCapabilityFloat16); // Remove all RelaxedPrecision decorations from instructions and globals diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp index fb5c1634..d877f0f9 100644 --- a/source/opt/dead_insert_elim_pass.cpp +++ b/source/opt/dead_insert_elim_pass.cpp @@ -256,7 +256,7 @@ Pass::Status DeadInsertElimPass::Process() { ProcessFunction pfn = [this](Function* fp) { return EliminateDeadInserts(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/inline_exhaustive_pass.cpp b/source/opt/inline_exhaustive_pass.cpp index f24f744d..bef45017 100644 --- a/source/opt/inline_exhaustive_pass.cpp +++ b/source/opt/inline_exhaustive_pass.cpp @@ -65,7 +65,7 @@ Pass::Status InlineExhaustivePass::ProcessImpl() { status = CombineStatus(status, InlineExhaustive(fp)); return false; }; - context()->ProcessEntryPointCallTree(pfn); + context()->ProcessReachableCallTree(pfn); return status; } diff --git a/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp index 6ccaf908..fe9c6799 100644 --- a/source/opt/inline_opaque_pass.cpp +++ b/source/opt/inline_opaque_pass.cpp @@ -105,7 +105,7 @@ Pass::Status InlineOpaquePass::ProcessImpl() { status = CombineStatus(status, InlineOpaque(fp)); return false; }; - context()->ProcessEntryPointCallTree(pfn); + context()->ProcessReachableCallTree(pfn); return status; } diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index 80b081dc..5fd4f658 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -223,7 +223,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() { return LocalSingleBlockLoadStoreElim(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index a32db3b7..051bcada 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -81,7 +81,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() { ProcessFunction pfn = [this](Function* fp) { return LocalSingleStoreElim(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/relax_float_ops_pass.cpp b/source/opt/relax_float_ops_pass.cpp index 73f16ddf..3fcf8795 100644 --- a/source/opt/relax_float_ops_pass.cpp +++ b/source/opt/relax_float_ops_pass.cpp @@ -76,7 +76,7 @@ Pass::Status RelaxFloatOpsPass::ProcessImpl() { Pass::ProcessFunction pfn = [this](Function* fp) { return ProcessFunction(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp index b9d2db3a..6903c4e7 100644 --- a/test/opt/block_merge_test.cpp +++ b/test/opt/block_merge_test.cpp @@ -90,6 +90,63 @@ OpFunctionEnd true); } +TEST_F(BlockMergeTest, BlockMergeForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%8 = OpTypeFunction %v4float %_ptr_Function_v4float +%main = OpFunction %v4float None %8 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%9 = OpLoad %v4float %BaseColor +OpStore %v %9 +OpBranch %10 +%10 = OpLabel +%11 = OpLoad %v4float %v +OpBranch %12 +%12 = OpLabel +OpReturnValue %11 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%8 = OpTypeFunction %v4float %_ptr_Function_v4float +%main = OpFunction %v4float None %8 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%9 = OpLoad %v4float %BaseColor +OpStore %v %9 +%11 = OpLoad %v4float %v +OpReturnValue %11 +OpFunctionEnd +)"; + SinglePassRunAndCheck(before, after, true, true); +} + TEST_F(BlockMergeTest, EmptyBlock) { // Note: SPIR-V hand edited to insert empty block // after two statements in main. diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp index ca6ee583..3a798f77 100644 --- a/test/opt/convert_relaxed_to_half_test.cpp +++ b/test/opt/convert_relaxed_to_half_test.cpp @@ -204,6 +204,98 @@ OpFunctionEnd defs_after + func_after, true, true); } +TEST_F(ConvertToHalfTest, ConvertToHalfForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_cbuff "type.cbuff" +OpMemberName %type_cbuff 0 "c" +OpName %cbuff "cbuff" +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %cbuff DescriptorSet 0 +OpDecorate %cbuff Binding 0 +OpMemberDecorate %type_cbuff 0 Offset 0 +OpDecorate %type_cbuff Block +OpDecorate %18 RelaxedPrecision +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%type_cbuff = OpTypeStruct %float +%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%9 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform +%main = OpFunction %v4float None %9 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +%16 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0 +%17 = OpLoad %float %16 +%18 = OpVectorTimesScalar %v4float %14 %17 +OpStore %v %18 +%19 = OpLoad %v4float %v +OpReturnValue %19 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpCapability Float16 +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_cbuff "type.cbuff" +OpMemberName %type_cbuff 0 "c" +OpName %cbuff "cbuff" +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %cbuff DescriptorSet 0 +OpDecorate %cbuff Binding 0 +OpMemberDecorate %type_cbuff 0 Offset 0 +OpDecorate %type_cbuff Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%type_cbuff = OpTypeStruct %float +%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +%main = OpFunction %v4float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +%17 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0 +%18 = OpLoad %float %17 +%22 = OpFConvert %v4half %16 +%23 = OpFConvert %half %18 +%7 = OpVectorTimesScalar %v4half %22 %23 +%24 = OpFConvert %v4float %7 +OpStore %v %24 +%19 = OpLoad %v4float %v +OpReturnValue %19 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} TEST_F(ConvertToHalfTest, ConvertToHalfWithDrefSample) { // The resulting SPIR-V was processed with --relax-float-ops. // diff --git a/test/opt/dead_insert_elim_test.cpp b/test/opt/dead_insert_elim_test.cpp index 9ea948a4..268e6590 100644 --- a/test/opt/dead_insert_elim_test.cpp +++ b/test/opt/dead_insert_elim_test.cpp @@ -170,6 +170,72 @@ OpFunctionEnd after_predefs + after, true, true); } +TEST_F(DeadInsertElimTest, DeadInsertForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%14 = OpTypeFunction %v2float %_ptr_Function_v2float +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %v2float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v2float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v2float Function +%16 = OpLoad %v2float %v +%17 = OpAccessChain %_ptr_Function_float %BaseColor %int_1 +%18 = OpLoad %float %17 +%19 = OpCompositeInsert %v2float %18 %16 0 +%20 = OpCompositeInsert %v2float %float_0 %19 0 +OpReturnValue %20 +OpFunctionEnd +)"; + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%14 = OpTypeFunction %v2float %_ptr_Function_v2float +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %v2float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v2float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v2float Function +%16 = OpLoad %v2float %v +%20 = OpCompositeInsert %v2float %float_0 %16 0 +OpReturnValue %20 +OpFunctionEnd +)"; + SinglePassRunAndCheck(before, after, true, true); +} + TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) { // Dead insert eliminated with phi in insertion chain. // diff --git a/test/opt/inline_opaque_test.cpp b/test/opt/inline_opaque_test.cpp index 8cb8925c..e4db4325 100644 --- a/test/opt/inline_opaque_test.cpp +++ b/test/opt/inline_opaque_test.cpp @@ -226,6 +226,115 @@ OpFunctionEnd predefs + before + post_defs, predefs + after + post_defs, true, true); } +TEST_F(InlineOpaqueTest, InlineOpaqueForLinkage) { + const std::string predefs_1 = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %foo_struct_S_t_vf2_vf21_ "foo(struct-S_t-vf2-vf21;" +OpName %s "s" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpName %param "param" +OpDecorate %main LinkageAttributes "main" Export +)"; + + const std::string name = R"(OpName %return_value "return_value" +)"; + + const std::string predefs_2 = R"(OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%13 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%19 = OpTypeSampledImage %18 +%S_t = OpTypeStruct %v2float %v2float %19 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%21 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 +%_ptr_Function_19 = OpTypePointer Function %19 +%sampler15 = OpVariable %_ptr_UniformConstant_19 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%return_value = OpFunctionCall %void %foo_struct_S_t_vf2_vf21_ %param +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%42 = OpAccessChain %_ptr_Function_19 %param %int_2 +%43 = OpLoad %19 %42 +%44 = OpAccessChain %_ptr_Function_v2float %param %int_0 +%45 = OpLoad %v2float %44 +%46 = OpImageSampleImplicitLod %v4float %43 %45 +OpStore %outColor %46 +OpReturn +OpFunctionEnd +)"; + + const std::string post_defs = + R"(%foo_struct_S_t_vf2_vf21_ = OpFunction %void None %21 +%s = OpFunctionParameter %_ptr_Function_S_t +%35 = OpLabel +%36 = OpAccessChain %_ptr_Function_19 %s %int_2 +%37 = OpLoad %19 %36 +%38 = OpAccessChain %_ptr_Function_v2float %s %int_0 +%39 = OpLoad %v2float %38 +%40 = OpImageSampleImplicitLod %v4float %37 %39 +OpStore %outColor %40 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_1 + name + predefs_2 + before + post_defs, + predefs_1 + predefs_2 + after + post_defs, true, true); +} + TEST_F(InlineOpaqueTest, InlineInNonEntryPointFunction) { // This demonstrates opaque inlining in a function that is not // an entry point function (main2) but is in the call tree of an diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index dfc5f098..719a3125 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -2581,6 +2581,132 @@ OpFunctionEnd SinglePassRunAndCheck(before, after, false, true); } +TEST_F(InlineTest, InlineForLinkage) { + const std::string before = + R"(OpCapability SampledBuffer +OpCapability ImageBuffer +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_buffer_image "type.buffer.image" +OpName %output "output" +OpName %main "main" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %param_var_color "param.var.color" +OpName %fn "fn" +OpName %color_0 "color" +OpName %bb_entry_0 "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %output DescriptorSet 0 +OpDecorate %output Binding 1 +%float = OpTypeFloat 32 +%float_0_200000003 = OpConstant %float 0.200000003 +%v4float = OpTypeVector %float 4 +%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Function_v4float = OpTypePointer Function %v4float +%11 = OpTypeFunction %float %_ptr_Function_v4float +%_ptr_Function_float = OpTypePointer Function %float +%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %float None %11 +%color = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%param_var_color = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %color +OpStore %param_var_color %16 +%17 = OpFunctionCall %float %fn %param_var_color +OpReturnValue %17 +OpFunctionEnd +%fn = OpFunction %float None %11 +%color_0 = OpFunctionParameter %_ptr_Function_v4float +%bb_entry_0 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %color_0 +OpStore %v %22 +%23 = OpLoad %v4float %v +%24 = OpFMul %v4float %23 %6 +OpStore %v %24 +%26 = OpAccessChain %_ptr_Function_float %v %int_0 +%27 = OpLoad %float %26 +OpReturnValue %27 +OpFunctionEnd + )"; + + const std::string after = + R"(OpCapability SampledBuffer +OpCapability ImageBuffer +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_buffer_image "type.buffer.image" +OpName %output "output" +OpName %main "main" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %param_var_color "param.var.color" +OpName %fn "fn" +OpName %color_0 "color" +OpName %bb_entry_0 "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %output DescriptorSet 0 +OpDecorate %output Binding 1 +%float = OpTypeFloat 32 +%float_0_200000003 = OpConstant %float 0.200000003 +%v4float = OpTypeVector %float 4 +%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Function_v4float = OpTypePointer Function %v4float +%11 = OpTypeFunction %float %_ptr_Function_v4float +%_ptr_Function_float = OpTypePointer Function %float +%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %float None %11 +%color = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%28 = OpVariable %_ptr_Function_v4float Function +%29 = OpVariable %_ptr_Function_float Function +%param_var_color = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %color +OpStore %param_var_color %16 +%31 = OpLoad %v4float %param_var_color +OpStore %28 %31 +%32 = OpLoad %v4float %28 +%33 = OpFMul %v4float %32 %6 +OpStore %28 %33 +%34 = OpAccessChain %_ptr_Function_float %28 %int_0 +%35 = OpLoad %float %34 +OpStore %29 %35 +%17 = OpLoad %float %29 +OpReturnValue %17 +OpFunctionEnd +%fn = OpFunction %float None %11 +%color_0 = OpFunctionParameter %_ptr_Function_v4float +%bb_entry_0 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %color_0 +OpStore %v %22 +%23 = OpLoad %v4float %v +%24 = OpFMul %v4float %23 %6 +OpStore %v %24 +%26 = OpAccessChain %_ptr_Function_float %v %int_0 +%27 = OpLoad %float %26 +OpReturnValue %27 +OpFunctionEnd +)"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, false, true); +} + TEST_F(InlineTest, InlineFuncWithOpTerminateRayNotInContinue) { const std::string text = R"( diff --git a/test/opt/local_single_block_elim.cpp b/test/opt/local_single_block_elim.cpp index 8e1cee61..28b8a07d 100644 --- a/test/opt/local_single_block_elim.cpp +++ b/test/opt/local_single_block_elim.cpp @@ -84,6 +84,56 @@ OpFunctionEnd predefs_before + before, predefs_before + after, true, true); } +TEST_F(LocalSingleBlockLoadStoreElimTest, LSBElimForLinkage) { + const std::string predefs_before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %main LinkageAttributes "main" Export +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +%15 = OpLoad %v4float %v +OpStore %gl_FragColor %15 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpStore %gl_FragColor %14 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + before, predefs_before + after, true, true); +} + TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleLoadLoadElim) { // #version 140 // diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp index c94ff372..5d910c4e 100644 --- a/test/opt/local_single_store_elim_test.cpp +++ b/test/opt/local_single_store_elim_test.cpp @@ -126,6 +126,91 @@ OpFunctionEnd predefs + after, true, true); } +TEST_F(LocalSingleStoreElimTest, LSSElimForLinkage) { + const std::string predefs = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %f "f" +OpName %fi "fi" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %main LinkageAttributes "main" Export +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%26 = OpLoad %v4float %v +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %26 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %20 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + TEST_F(LocalSingleStoreElimTest, ThreeStores) { // Three stores to multiple loads of v is not optimized. diff --git a/test/opt/relax_float_ops_test.cpp b/test/opt/relax_float_ops_test.cpp index 14cde0b9..b9cb0de0 100644 --- a/test/opt/relax_float_ops_test.cpp +++ b/test/opt/relax_float_ops_test.cpp @@ -137,6 +137,86 @@ OpFunctionEnd true); } +TEST_F(RelaxFloatOpsTest, RelaxFloatOpsForLinkage) { + const std::string defs0 = + R"(OpCapability Shader +OpCapability Linkage +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %i_Tex0 "i.Tex0" +OpName %i_Tex1 "i.Tex1" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpDecorate %i_Tex0 Location 0 +OpDecorate %i_Tex1 Location 1 +OpDecorate %_entryPointOutput_Color Location 0 +)"; + + const std::string defs1 = + R"(%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%17 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant +%25 = OpTypeSampledImage %17 +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%i_Tex1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%float_0_5 = OpConstant %float 0.5 +%116 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +)"; + + const std::string relax_decos = + R"(OpDecorate %60 RelaxedPrecision +OpDecorate %63 RelaxedPrecision +OpDecorate %82 RelaxedPrecision +OpDecorate %88 RelaxedPrecision +OpDecorate %91 RelaxedPrecision +OpDecorate %94 RelaxedPrecision +)"; + + const std::string func_orig = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%60 = OpLoad %float %i_Tex0 +%63 = OpLoad %float %i_Tex1 +%77 = OpLoad %17 %g_tTex1df4 +%78 = OpLoad %21 %g_sSamp +%79 = OpSampledImage %25 %77 %78 +%82 = OpImageSampleImplicitLod %v4float %79 %60 +%83 = OpLoad %17 %g_tTex1df4 +%84 = OpLoad %21 %g_sSamp +%85 = OpSampledImage %25 %83 %84 +%88 = OpImageSampleImplicitLod %v4float %85 %63 +%91 = OpFAdd %v4float %82 %88 +%94 = OpFMul %v4float %91 %116 +OpStore %_entryPointOutput_Color %94 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs0 + defs1 + func_orig, defs0 + relax_decos + defs1 + func_orig, true, + true); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From 4578db3c419a9300485155fd8b81f6b1d822b5fb Mon Sep 17 00:00:00 2001 From: David Neto Date: Mon, 18 Oct 2021 15:34:06 -0400 Subject: README: Update tested versions of compilers (#4579) I checked this against the Kokoro output. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1e3f7745..3a309bf5 100644 --- a/README.md +++ b/README.md @@ -382,11 +382,11 @@ also work, but are not verified. SPIRV-Tools is regularly tested with the following compilers: On Linux -- GCC version 4.8.5 -- Clang version 3.8 +- GCC version 9.3 +- Clang version 10.0 On MacOS -- AppleClang 10.0 +- AppleClang 11.0 On Windows - Visual Studio 2015 -- cgit v1.2.3 From 7a7a69037e72e6fcf08cf214fddc3e751de8cc9d Mon Sep 17 00:00:00 2001 From: David Neto Date: Tue, 19 Oct 2021 17:17:56 -0400 Subject: README: Remove appveyor build badge. (#4584) Bot failures are unrelated. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3a309bf5..f53db165 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ headers, and XML registry. ## Downloads -[![Build status](https://ci.appveyor.com/api/projects/status/gpue87cesrx3pi0d/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master) Linux[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) MacOS[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) Windows[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) -- cgit v1.2.3 From 001604bd4a89160fe48a211565e5b0c8019df34a Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Tue, 19 Oct 2021 18:27:16 -0600 Subject: Generate constants directly in CreateDebugInlinedAt (#4572) Do this if Constant or DefUse managers are invalid. Using the ConstantManager attempts to regenerate the DefUseManager which is not valid during inlining. --- source/opt/debug_info_manager.cpp | 33 +++++++++- test/opt/debug_info_manager_test.cpp | 116 +++++++++++++++++++++++++++++++++++ test/opt/inline_test.cpp | 99 ++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+), 3 deletions(-) diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index 6593c3fb..060e0d93 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -146,6 +146,25 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, } } +// Create new constant directly into global value area, bypassing the +// Constant manager. This is used when the DefUse or Constant managers +// are invalid and cannot be regenerated due to the module being in an +// inconsistant state e.g. in the middle of significant modification +// such as inlining. Invalidate Constant and DefUse managers if used. +uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) { + uint32_t id = context->TakeNextId(); + std::unique_ptr new_const(new Instruction( + context, SpvOpConstant, context->get_type_mgr()->GetUIntTypeId(), id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + {const_value}}, + })); + context->module()->AddGlobalValue(std::move(new_const)); + context->InvalidateAnalyses(IRContext::kAnalysisConstants); + context->InvalidateAnalyses(IRContext::kAnalysisDefUse); + return id; +} + uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, const DebugScope& scope) { uint32_t setId = GetDbgSetImportId(); @@ -194,10 +213,18 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); // If we need the line number as an ID, generate that constant now. + // If Constant or DefUse managers are invalid, generate constant + // directly into the global value section of the module; do not + // use Constant manager which may attempt to invoke building of the + // DefUse manager which cannot be done during inlining. The extra + // constants that may be generated here is likely not significant + // and will likely be cleaned up in later passes. if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID) { - uint32_t line_id = - context()->get_constant_mgr()->GetUIntConst(line_number); - line_number = line_id; + if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) || + !context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants)) + line_number = AddNewConstInGlobals(context(), line_number); + else + line_number = context()->get_constant_mgr()->GetUIntConst(line_number); } } diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp index 49407fd5..e87d0bea 100644 --- a/test/opt/debug_info_manager_test.cpp +++ b/test/opt/debug_info_manager_test.cpp @@ -185,6 +185,122 @@ void main(float in_var_color : COLOR) { 100U); } +TEST(DebugInfoManager, CreateDebugInlinedAtWithConstantManager) { + // Show that CreateDebugInlinedAt will use the Constant manager to generate + // its line operand if the Constant and DefUse managers are valid. This is + // proven by checking that the id for the line operand 7 is the same as the + // existing constant 7. + // + // int function1() { + // return 1; + // } + // + // void main() { + // function1(); + // } + const std::string text = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "parent3.hlsl" +%8 = OpString "int" +%19 = OpString "function1" +%20 = OpString "" +%26 = OpString "main" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %function1 "function1" +OpName %bb_entry_0 "bb.entry" +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_2 = OpConstant %uint 2 +%uint_17 = OpConstant %uint 17 +%uint_6 = OpConstant %uint 6 +%uint_13 = OpConstant %uint 13 +%100 = OpConstant %uint 7 +%31 = OpTypeFunction %void +%42 = OpTypeFunction %int +%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0 +%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10 +%15 = OpExtInst %void %1 DebugSource %3 +%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 +%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2 +%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21 +%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6 +%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27 +%main = OpFunction %void None %31 +%32 = OpLabel +%33 = OpFunctionCall %void %src_main +OpLine %3 8 1 +OpReturn +OpFunctionEnd +OpLine %3 6 1 +%src_main = OpFunction %void None %31 +OpNoLine +%bb_entry = OpLabel +%47 = OpExtInst %void %1 DebugScope %27 +%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main +%48 = OpExtInst %void %1 DebugScope %29 +OpLine %3 7 3 +%39 = OpFunctionCall %int %function1 +%49 = OpExtInst %void %1 DebugScope %27 +OpLine %3 8 1 +OpReturn +%50 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +OpLine %3 2 1 +%function1 = OpFunction %int None %42 +OpNoLine +%bb_entry_0 = OpLabel +%51 = OpExtInst %void %1 DebugScope %21 +%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1 +%52 = OpExtInst %void %1 DebugScope %23 +OpLine %3 3 3 +OpReturnValue %int_1 +%53 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + const uint32_t line_number = 7U; + Instruction line(context.get(), SpvOpLine); + line.SetInOperands({ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}}, + }); + + DebugScope scope(29U, 0U); + + auto db_manager = context.get()->get_debug_info_mgr(); + auto du_manager = context.get()->get_def_use_mgr(); + auto c_manager = context.get()->get_constant_mgr(); + + (void)du_manager; + (void)c_manager; + + uint32_t inlined_at_id = db_manager->CreateDebugInlinedAt(&line, scope); + auto* inlined_at = db_manager->GetDebugInlinedAt(inlined_at_id); + EXPECT_NE(inlined_at, nullptr); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), + 100); +} + TEST(DebugInfoManager, GetDebugInfoNone) { const std::string text = R"( OpCapability Shader diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index 719a3125..6b48f280 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -4236,6 +4236,105 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, CreateConstantForInlinedAt) { + // This shader causes CreateDebugInlinedAt to generate a constant. + // Using the Constant manager would attempt to build the invalidated + // DefUse manager during inlining which could cause an assert because + // the function is in an inconsistant state. This test verifies that + // CreateDebugInlinedAt detects that the DefUse manager is disabled + // and creates a duplicate constant safely without the Constant manager. + // + // int function1() { + // return 1; + // } + // + // void main() { + // function1(); + // } + + const std::string text = R"(OpCapability Shader +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_7_0 = OpConstant %uint 7 +; CHECK: OpExtInst %void %1 DebugInlinedAt %uint_7_0 +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "parent3.hlsl" +%8 = OpString "int" +%19 = OpString "function1" +%20 = OpString "" +%26 = OpString "main" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %function1 "function1" +OpName %bb_entry_0 "bb.entry" +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_2 = OpConstant %uint 2 +%uint_17 = OpConstant %uint 17 +%uint_6 = OpConstant %uint 6 +%uint_13 = OpConstant %uint 13 +%uint_7 = OpConstant %uint 7 +%31 = OpTypeFunction %void +%42 = OpTypeFunction %int +%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0 +%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10 +%15 = OpExtInst %void %1 DebugSource %3 +%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 +%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2 +%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21 +%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6 +%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27 +%main = OpFunction %void None %31 +%32 = OpLabel +%33 = OpFunctionCall %void %src_main +OpLine %3 8 1 +OpReturn +OpFunctionEnd +OpLine %3 6 1 +%src_main = OpFunction %void None %31 +OpNoLine +%bb_entry = OpLabel +%47 = OpExtInst %void %1 DebugScope %27 +%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main +%48 = OpExtInst %void %1 DebugScope %29 +OpLine %3 7 3 +%39 = OpFunctionCall %int %function1 +%49 = OpExtInst %void %1 DebugScope %27 +OpLine %3 8 1 +OpReturn +%50 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +OpLine %3 2 1 +%function1 = OpFunction %int None %42 +OpNoLine +%bb_entry_0 = OpLabel +%51 = OpExtInst %void %1 DebugScope %21 +%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1 +%52 = OpExtInst %void %1 DebugScope %23 +OpLine %3 3 3 +OpReturnValue %int_1 +%53 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // Empty modules -- cgit v1.2.3 From 0f3bc1d9b2f8fa43dee8c13c16ea774c9683c39c Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 20 Oct 2021 15:20:07 +0100 Subject: Fix i386 build issues related to random generation (#4586) The OSS-Fuzz i386 build has been failing due to errors about 64-to-32-bit conversions, relating to random generation code. This changre fixes the problem by explicitly using a 64-bit random generator, and by adding a cast to size_t to avoid an implicit conversion. --- test/fuzzers/random_generator.cpp | 5 +++-- test/fuzzers/random_generator.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/fuzzers/random_generator.cpp b/test/fuzzers/random_generator.cpp index 878961fc..ec1723e9 100644 --- a/test/fuzzers/random_generator.cpp +++ b/test/fuzzers/random_generator.cpp @@ -29,7 +29,7 @@ namespace { /// @param upper - Upper bound of integer generated /// @returns i, where lower <= i < upper template -I RandomUInt(std::mt19937* engine, I lower, I upper) { +I RandomUInt(std::mt19937_64* engine, I lower, I upper) { assert(lower < upper && "|lower| must be stictly less than |upper|"); return std::uniform_int_distribution(lower, upper - 1)(*engine); } @@ -70,7 +70,8 @@ void HashCombine(size_t* hash, const T& value) { /// @param size - number of elements in buffer /// @returns hash of the data in the buffer size_t HashBuffer(const uint8_t* data, const size_t size) { - size_t hash = 0xCA8945571519E991; // seed with an arbitrary prime + size_t hash = + static_cast(0xCA8945571519E991); // seed with an arbitrary prime HashCombine(&hash, size); for (size_t i = 0; i < size; i++) { HashCombine(&hash, data[i]); diff --git a/test/fuzzers/random_generator.h b/test/fuzzers/random_generator.h index 61710556..868dcb14 100644 --- a/test/fuzzers/random_generator.h +++ b/test/fuzzers/random_generator.h @@ -48,7 +48,7 @@ class RandomGenerator { spv_target_env GetTargetEnv(); private: - std::mt19937 engine_; + std::mt19937_64 engine_; }; // class RandomGenerator } // namespace fuzzers -- cgit v1.2.3 From f3fbd98ff52da2c56f706be7ee09d9a09dcb4742 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 21 Oct 2021 01:53:24 +1000 Subject: opt/spec_constants: fix bit pattern width checks. (#4588) * test: add a test to show 8/16-bit * opt/spec_constants: fix bit pattern width checks. The input bit patterns are always at least 32-bits, so let the test pass for 8/16-bit values as well. This shouldn't have any effect on the 64-bit patterns I assume this was introduced for. --- .../opt/set_spec_constant_default_value_pass.cpp | 8 +++- test/opt/set_spec_const_default_value_test.cpp | 44 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp index 4c8d116f..83712b36 100644 --- a/source/opt/set_spec_constant_default_value_pass.cpp +++ b/source/opt/set_spec_constant_default_value_pass.cpp @@ -98,11 +98,15 @@ std::vector ParseDefaultValueBitPattern( } return result; } else if (const auto* IT = type->AsInteger()) { - if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + auto width = IT->width(); + if (width == 8 || width == 16) width = 32; + if (width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { return std::vector(input_bit_pattern); } } else if (const auto* FT = type->AsFloat()) { - if (FT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + auto width = FT->width(); + if (width == 8 || width == 16) width = 32; + if (width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { return std::vector(input_bit_pattern); } } diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp index 5e63862e..58acd431 100644 --- a/test/opt/set_spec_const_default_value_test.cpp +++ b/test/opt/set_spec_const_default_value_test.cpp @@ -935,6 +935,50 @@ INSTANTIATE_TEST_SUITE_P( "%2 = OpSpecConstantTrue %bool\n" "%3 = OpSpecConstantTrue %bool\n", }, + // 19. 16-bit int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%short = OpTypeInt 16 1\n" + "%1 = OpSpecConstant %short 10\n" + "%2 = OpSpecConstant %short 11\n" + "%3 = OpSpecConstant %short 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {32768}}, {101, {0xffff}}, {102, {0xffffffd6}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%short = OpTypeInt 16 1\n" + "%1 = OpSpecConstant %short 32768\n" + "%2 = OpSpecConstant %short 65535\n" + "%3 = OpSpecConstant %short -42\n", + }, + // 20. 8-bit int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%char = OpTypeInt 8 1\n" + "%1 = OpSpecConstant %char 10\n" + "%2 = OpSpecConstant %char 11\n" + "%3 = OpSpecConstant %char 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {128}}, {101, {129}}, {102, {0xffffffd6}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%char = OpTypeInt 8 1\n" + "%1 = OpSpecConstant %char 128\n" + "%2 = OpSpecConstant %char 129\n" + "%3 = OpSpecConstant %char -42\n", + }, })); INSTANTIATE_TEST_SUITE_P( -- cgit v1.2.3 From 7326b494d079cfd24d08e46e291754151e2b9462 Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 21 Oct 2021 09:44:54 -0400 Subject: opt: set upper bits of spec constant according to spec (#4589) When setting default value for spec constants, for numeric bit types smaller than 32 bits, follow the SPIR-V rules for narrow literals: - signed integers are sign-extended - otherwise, upper bits are zero. Followup to #4588 --- .../opt/set_spec_constant_default_value_pass.cpp | 37 +++++++++--- test/opt/set_spec_const_default_value_test.cpp | 68 ++++++++++++++++++---- 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp index 83712b36..4def2b09 100644 --- a/source/opt/set_spec_constant_default_value_pass.cpp +++ b/source/opt/set_spec_constant_default_value_pass.cpp @@ -85,6 +85,10 @@ std::vector ParseDefaultValueStr(const char* text, // with 0x1, which represents a 'true'. // If all words in the bit pattern are zero, returns a bit pattern with 0x0, // which represents a 'false'. +// For integer and floating point types narrower than 32 bits, the upper bits +// in the input bit pattern are ignored. Instead the upper bits are set +// according to SPIR-V literal requirements: sign extend a signed integer, and +// otherwise set the upper bits to zero. std::vector ParseDefaultValueBitPattern( const std::vector& input_bit_pattern, const analysis::Type* type) { @@ -98,16 +102,33 @@ std::vector ParseDefaultValueBitPattern( } return result; } else if (const auto* IT = type->AsInteger()) { - auto width = IT->width(); - if (width == 8 || width == 16) width = 32; - if (width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { - return std::vector(input_bit_pattern); + const auto width = IT->width(); + assert(width > 0); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + const uint32_t high_active_bit = (1u << width) >> 1; + if (IT->IsSigned() && (high_active_bit & result[0])) { + // Sign extend. This overwrites the sign bit again, but that's ok. + result[0] = result[0] | ~(high_active_bit - 1); + } else { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + } + return result; } } else if (const auto* FT = type->AsFloat()) { - auto width = FT->width(); - if (width == 8 || width == 16) width = 32; - if (width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { - return std::vector(input_bit_pattern); + const auto width = FT->width(); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + return result; } } result.clear(); diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp index 58acd431..f1dd50ee 100644 --- a/test/opt/set_spec_const_default_value_test.cpp +++ b/test/opt/set_spec_const_default_value_test.cpp @@ -935,7 +935,7 @@ INSTANTIATE_TEST_SUITE_P( "%2 = OpSpecConstantTrue %bool\n" "%3 = OpSpecConstantTrue %bool\n", }, - // 19. 16-bit int type. + // 19. 16-bit signed int type. { // code "OpDecorate %1 SpecId 100\n" @@ -947,17 +947,39 @@ INSTANTIATE_TEST_SUITE_P( "%3 = OpSpecConstant %short 11\n", // default values SpecIdToValueBitPatternMap{ - {100, {32768}}, {101, {0xffff}}, {102, {0xffffffd6}}}, - // expected + {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}}, + // expected. These are sign-extended "OpDecorate %1 SpecId 100\n" "OpDecorate %2 SpecId 101\n" "OpDecorate %3 SpecId 102\n" "%short = OpTypeInt 16 1\n" - "%1 = OpSpecConstant %short 32768\n" - "%2 = OpSpecConstant %short 65535\n" + "%1 = OpSpecConstant %short 32767\n" + "%2 = OpSpecConstant %short -1\n" "%3 = OpSpecConstant %short -42\n", }, - // 20. 8-bit int type. + // 20. 16-bit unsigned int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%ushort = OpTypeInt 16 0\n" + "%1 = OpSpecConstant %ushort 10\n" + "%2 = OpSpecConstant %ushort 11\n" + "%3 = OpSpecConstant %ushort 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}}, + // expected. Upper bits are always zero. + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%ushort = OpTypeInt 16 0\n" + "%1 = OpSpecConstant %ushort 32767\n" + "%2 = OpSpecConstant %ushort 65535\n" + "%3 = OpSpecConstant %ushort 65494\n", + }, + // 21. 8-bit signed int type. { // code "OpDecorate %1 SpecId 100\n" @@ -969,16 +991,42 @@ INSTANTIATE_TEST_SUITE_P( "%3 = OpSpecConstant %char 11\n", // default values SpecIdToValueBitPatternMap{ - {100, {128}}, {101, {129}}, {102, {0xffffffd6}}}, - // expected + {100, {127}}, {101, {128}}, {102, {0xd6}}}, + // expected. These are sign extended "OpDecorate %1 SpecId 100\n" "OpDecorate %2 SpecId 101\n" "OpDecorate %3 SpecId 102\n" "%char = OpTypeInt 8 1\n" - "%1 = OpSpecConstant %char 128\n" - "%2 = OpSpecConstant %char 129\n" + "%1 = OpSpecConstant %char 127\n" + "%2 = OpSpecConstant %char -128\n" "%3 = OpSpecConstant %char -42\n", }, + // 22. 8-bit unsigned int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "%uchar = OpTypeInt 8 0\n" + "%1 = OpSpecConstant %uchar 10\n" + "%2 = OpSpecConstant %uchar 11\n" + "%3 = OpSpecConstant %uchar 11\n" + "%4 = OpSpecConstant %uchar 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {127}}, {101, {128}}, {102, {256}}, {103, {0xffffffd6}}}, + // expected. Upper bits are always zero. + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "%uchar = OpTypeInt 8 0\n" + "%1 = OpSpecConstant %uchar 127\n" + "%2 = OpSpecConstant %uchar 128\n" + "%3 = OpSpecConstant %uchar 0\n" + "%4 = OpSpecConstant %uchar 214\n", + }, })); INSTANTIATE_TEST_SUITE_P( -- cgit v1.2.3 From 17a5bacfa7ab0470f45097ce1d3769fe7ea9211c Mon Sep 17 00:00:00 2001 From: alan-baker Date: Mon, 25 Oct 2021 17:40:11 -0400 Subject: Handle missing execution modes for limitation check (#4594) Fixes https://crbug.com/40216 * Handle entry points with no execution modes when checking implicit derivative execution model checks --- source/val/validate_image.cpp | 12 +++++++----- test/val/val_image_test.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 3baff820..64f6ba7b 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -2068,11 +2068,13 @@ spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) { std::string* message) { const auto* models = state.GetExecutionModels(entry_point->id()); const auto* modes = state.GetExecutionModes(entry_point->id()); - if (models->find(SpvExecutionModelGLCompute) != models->end() && - modes->find(SpvExecutionModeDerivativeGroupLinearNV) == - modes->end() && - modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == - modes->end()) { + if (models && + models->find(SpvExecutionModelGLCompute) != models->end() && + (!modes || + (modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()))) { if (message) { *message = std::string( diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index f49c81a5..11b14fb0 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -6063,6 +6063,42 @@ TEST_F(ValidateImage, ImageTexelPointerRgba16fVulkan) { "R32f, R32i, or R32ui for Vulkan environment")); } +TEST_F(ValidateImage, ImageExecutionModeLimitationNoMode) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 " " %4 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui +%13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%4 = OpVariable %_ptr_Input_v4float Input +%v2float = OpTypeVector %float 2 +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%2 = OpFunction %void None %8 +%8224 = OpLabel +%6 = OpLoad %13 %5 +%19 = OpLoad %v4float %4 +%20 = OpVectorShuffle %v2float %19 %19 0 1 +%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19 +%65312 = OpImageSampleImplicitLod %v4float %6 %21 +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImplicitLod instructions require " + "DerivativeGroupQuadsNV or DerivativeGroupLinearNV " + "execution mode for GLCompute execution model")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 8c91f14a9b76a725d092f6e1412ce9edf6e0ab9e Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 26 Oct 2021 16:24:51 +0100 Subject: Add libFuzzer target for spirv-fuzz (#4434) Fixes #4431. --- test/fuzzers/CMakeLists.txt | 3 ++ test/fuzzers/random_generator.cpp | 9 ++++ test/fuzzers/random_generator.h | 11 +++++ test/fuzzers/spvtools_fuzz_fuzzer.cpp | 80 +++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 test/fuzzers/spvtools_fuzz_fuzzer.cpp diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt index a97c8db1..50c45895 100644 --- a/test/fuzzers/CMakeLists.txt +++ b/test/fuzzers/CMakeLists.txt @@ -50,4 +50,7 @@ if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + if (${SPIRV_BUILD_FUZZER}) + add_spvtools_libfuzzer_target(TARGET spvtools_fuzz_fuzzer SRCS spvtools_fuzz_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY}) + endif() endif() diff --git a/test/fuzzers/random_generator.cpp b/test/fuzzers/random_generator.cpp index ec1723e9..801a9ffe 100644 --- a/test/fuzzers/random_generator.cpp +++ b/test/fuzzers/random_generator.cpp @@ -99,6 +99,15 @@ spv_target_env RandomGenerator::GetTargetEnv() { return result; } +uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) { + return RandomUInt(&engine_, lower, upper); +} + +uint32_t RandomGenerator::GetUInt32(uint32_t bound) { + assert(bound > 0 && "|bound| must be greater than 0"); + return RandomUInt(&engine_, 0u, bound); +} + uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) { assert(data != nullptr && "|data| must be !nullptr"); diff --git a/test/fuzzers/random_generator.h b/test/fuzzers/random_generator.h index 868dcb14..b121fe8c 100644 --- a/test/fuzzers/random_generator.h +++ b/test/fuzzers/random_generator.h @@ -47,6 +47,17 @@ class RandomGenerator { /// Get random valid target env. spv_target_env GetTargetEnv(); + /// Get uint32_t value from uniform distribution. + /// @param lower - lower bound of integer generated + /// @param upper - upper bound of integer generated + /// @returns i, where lower <= i < upper + uint32_t GetUInt32(uint32_t lower, uint32_t upper); + + /// Get uint32_t value from uniform distribution. + /// @param bound - Upper bound of integer generated + /// @returns i, where 0 <= i < bound + uint32_t GetUInt32(uint32_t bound); + private: std::mt19937_64 engine_; }; // class RandomGenerator diff --git a/test/fuzzers/spvtools_fuzz_fuzzer.cpp b/test/fuzzers/spvtools_fuzz_fuzzer.cpp new file mode 100644 index 00000000..d43920cf --- /dev/null +++ b/test/fuzzers/spvtools_fuzz_fuzzer.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2021 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 +#include + +#include "source/fuzz/fuzzer.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size == 0 || (size % sizeof(uint32_t)) != 0) { + // An empty binary, or a binary whose size is not a multiple of word-size, + // cannot be valid, so can be rejected immediately. + return 0; + } + + std::vector initial_binary(size / sizeof(uint32_t)); + memcpy(initial_binary.data(), data, size); + + spvtools::ValidatorOptions validator_options; + + spvtools::MessageConsumer message_consumer = + [](spv_message_level_t, const char*, const spv_position_t&, const char*) { + }; + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + auto target_env = random_gen.GetTargetEnv(); + std::unique_ptr ir_context; + if (!spvtools::fuzz::fuzzerutil::BuildIRContext( + target_env, message_consumer, initial_binary, validator_options, + &ir_context)) { + // The input is invalid - give up. + return 0; + } + + std::vector donor_suppliers = { + [&initial_binary, message_consumer, target_env, + &validator_options]() -> std::unique_ptr { + std::unique_ptr result; + if (!spvtools::fuzz::fuzzerutil::BuildIRContext( + target_env, message_consumer, initial_binary, validator_options, + &result)) { + // The input was successfully parsed and validated first time around, + // so something is wrong if it is now invalid. + abort(); + } + return result; + }}; + + uint32_t seed = random_gen.GetUInt32(std::numeric_limits::max()); + auto fuzzer_context = spvtools::MakeUnique( + spvtools::MakeUnique(seed), + spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()), false); + + auto transformation_context = + spvtools::MakeUnique( + spvtools::MakeUnique(ir_context.get()), + validator_options); + + spvtools::fuzz::Fuzzer fuzzer( + std::move(ir_context), std::move(transformation_context), + std::move(fuzzer_context), message_consumer, donor_suppliers, false, + spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations, true, + validator_options); + fuzzer.Run(0); + return 0; +} -- cgit v1.2.3 From d78c1c4cd3cee1f794dd6bd61b3485f8cd28ec42 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 26 Oct 2021 13:24:29 -0400 Subject: Make IsLocalVar in ADCE work at any time. (NFC) (#4595) Having IsLocalVar work only sometimes is something that could easily lead to an error. This change refactors the code so that the function can be called at any point. The current implementation was used because we did not want to do multiple searches to see if a function was an entry point or if it had a call. This was maintained by added a cache that will store of a given function is an entry point with no calls. --- source/opt/aggressive_dead_code_elim_pass.cpp | 86 +++++++++++++-------------- source/opt/aggressive_dead_code_elim_pass.h | 25 +++++--- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 2eac075d..c83fb48c 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -96,16 +96,21 @@ bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) { storageClass; } -bool AggressiveDCEPass::IsLocalVar(uint32_t varId) { +bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) { if (IsVarOfStorage(varId, SpvStorageClassFunction)) { return true; } - if (!private_like_local_) { + + if (!IsVarOfStorage(varId, SpvStorageClassPrivate) && + !IsVarOfStorage(varId, SpvStorageClassWorkgroup)) { return false; } - return IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup); + // For a variable in the Private or WorkGroup storage class, the variable will + // get a new instance for every call to an entry point. If the entry point + // does not have a call, then no other function can read or write to that + // instance of the variable. + return IsEntryPointWithNoCalls(func); } void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { @@ -190,7 +195,7 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { // Only process locals - if (!IsLocalVar(varId)) return; + if (!IsLocalVar(varId, func)) return; // Return if already processed if (live_local_vars_.find(varId) != live_local_vars_.end()) return; // Mark all stores to varId as live @@ -504,14 +509,6 @@ void AggressiveDCEPass::InitializeWorkList( // branches that are not attached to a structured construct. // TODO(s-perron): The handling of branch seems to be adhoc. This needs to be // cleaned up. - bool call_in_func = false; - bool func_is_entry_point = false; - - // TODO(s-perron): We need to check if this is actually needed. In cases - // where private variable can be treated as if they are function scope, the - // private-to-local pass should be able to change them to function scope. - std::vector private_stores; - for (auto& bi : structured_order) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { SpvOp op = ii->opcode(); @@ -519,26 +516,15 @@ void AggressiveDCEPass::InitializeWorkList( case SpvOpStore: { uint32_t var_id = 0; (void)GetPtr(&*ii, &var_id); - // Mark stores as live if their variable is not function scope - // and is not private scope. Remember private stores for possible - // later inclusion. We cannot call IsLocalVar at this point because - // private_like_local_ has not been set yet. - if (IsVarOfStorage(var_id, SpvStorageClassPrivate) || - IsVarOfStorage(var_id, SpvStorageClassWorkgroup)) - private_stores.push_back(&*ii); - else if (!IsVarOfStorage(var_id, SpvStorageClassFunction)) - AddToWorklist(&*ii); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); } break; case SpvOpCopyMemory: case SpvOpCopyMemorySized: { uint32_t var_id = 0; - (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx), - &var_id); - if (IsVarOfStorage(var_id, SpvStorageClassPrivate) || - IsVarOfStorage(var_id, SpvStorageClassWorkgroup)) - private_stores.push_back(&*ii); - else if (!IsVarOfStorage(var_id, SpvStorageClassFunction)) - AddToWorklist(&*ii); + uint32_t target_addr_id = + ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx); + (void)GetPtr(target_addr_id, &var_id); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); } break; case SpvOpSwitch: case SpvOpBranch: @@ -559,26 +545,10 @@ void AggressiveDCEPass::InitializeWorkList( if (!ii->IsOpcodeSafeToDelete()) { AddToWorklist(&*ii); } - // Remember function calls - if (op == SpvOpFunctionCall) call_in_func = true; } break; } } } - // See if current function is an entry point - for (auto& ei : get_module()->entry_points()) { - if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) == - func->result_id()) { - func_is_entry_point = true; - break; - } - } - // If the current function is an entry point and has no function calls, - // we can optimize private variables as locals - private_like_local_ = func_is_entry_point && !call_in_func; - // If privates are not like local, add their stores to worklist - if (!private_like_local_) - for (auto& ps : private_stores) AddToWorklist(ps); } void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { @@ -1067,5 +1037,31 @@ bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block, return false; } +bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) { + auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id()); + if (cached_result != entry_point_with_no_calls_cache_.end()) { + return cached_result->second; + } + bool result = IsEntryPoint(func) && !HasCall(func); + entry_point_with_no_calls_cache_[func->result_id()] = result; + return result; +} + +bool AggressiveDCEPass::IsEntryPoint(Function* func) { + for (const Instruction& entry_point : get_module()->entry_points()) { + uint32_t entry_point_id = + entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + if (entry_point_id == func->result_id()) { + return true; + } + } + return false; +} + +bool AggressiveDCEPass::HasCall(Function* func) { + return !func->WhileEachInst( + [](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; }); +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index e0a9b524..7d1679f6 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h @@ -67,10 +67,10 @@ class AggressiveDCEPass : public MemPass { // be 0 or the result of an instruction. bool IsVarOfStorage(uint32_t varId, uint32_t storageClass); - // Return true if |varId| is variable of function storage class or is - // private variable and privates can be optimized like locals (see - // privates_like_local_). - bool IsLocalVar(uint32_t varId); + // Return true if the instance of the variable |varId| can only be access in + // |func|. For example, a function scope variable, or a private variable + // where |func| is an entry point with no function calls. + bool IsLocalVar(uint32_t varId, Function* func); // Return true if |inst| is marked live. bool IsLive(const Instruction* inst) const { @@ -157,7 +157,7 @@ class AggressiveDCEPass : public MemPass { void MarkBlockAsLive(Instruction* inst); // Marks any variables from which |inst| may require data as live. - void MarkLoadedVariablesAsLive(Function* opernad_id, Instruction* inst); + void MarkLoadedVariablesAsLive(Function* func, Instruction* inst); // Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a // value whose type is a pointer. @@ -207,8 +207,19 @@ class AggressiveDCEPass : public MemPass { // Returns true if |bb| is in the construct with header |header_block|. bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb); - // True if current function is entry point and has no function calls. - bool private_like_local_; + // Returns true if |func| is an entry point that does not have any function + // calls. + bool IsEntryPointWithNoCalls(Function* func); + + // Returns true if |func| is an entry point. + bool IsEntryPoint(Function* func); + + // Returns true if |func| contains a function call. + bool HasCall(Function* func); + + // The cached results for |IsEntryPointWithNoCalls|. It maps the function's + // result id to the return value. + std::unordered_map entry_point_with_no_calls_cache_; // Live Instruction Worklist. An instruction is added to this list // if it might have a side effect, either directly or indirectly. -- cgit v1.2.3 From d997c83b103ed1f3af09ed65e1cbf89fbc6d9451 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Tue, 26 Oct 2021 17:20:58 -0400 Subject: Add spirv-opt pass to replace descriptor accesses based on variable indices (#4574) This commit adds a spirv-opt pass to replace accesses to descriptor array based on variable indices with constant elements. Before: ``` %descriptor = OpVariable %_ptr_array_Image Uniform ... %ac = OpAccessChain %_ptr_Image %descriptor %variable_index (some image instructions using %ac) ``` After: ``` %descriptor = OpVariable %_ptr_array_Image Uniform ... OpSwitch %variable_index 0 %case0 1 %case1 ... ... %case0 = OpLabel %ac = OpAccessChain %_ptr_Image %descriptor %uint_0 ... %case1 = OpLabel %ac = OpAccessChain %_ptr_Image %descriptor %uint_1 ... (use OpPhi for value with concrete type) ``` --- Android.mk | 2 + BUILD.gn | 4 + include/spirv-tools/optimizer.hpp | 7 + source/opt/CMakeLists.txt | 4 + source/opt/decoration_manager.cpp | 8 + source/opt/decoration_manager.h | 4 + source/opt/desc_sroa.cpp | 119 +----- source/opt/desc_sroa.h | 9 - source/opt/desc_sroa_util.cpp | 117 ++++++ source/opt/desc_sroa_util.h | 54 +++ source/opt/optimizer.cpp | 7 + source/opt/passes.h | 1 + .../replace_desc_array_access_using_var_index.cpp | 423 +++++++++++++++++++++ .../replace_desc_array_access_using_var_index.h | 204 ++++++++++ test/opt/CMakeLists.txt | 1 + ...lace_desc_array_access_using_var_index_test.cpp | 411 ++++++++++++++++++++ tools/opt/opt.cpp | 5 + 17 files changed, 1265 insertions(+), 115 deletions(-) create mode 100644 source/opt/desc_sroa_util.cpp create mode 100644 source/opt/desc_sroa_util.h create mode 100644 source/opt/replace_desc_array_access_using_var_index.cpp create mode 100644 source/opt/replace_desc_array_access_using_var_index.h create mode 100644 test/opt/replace_desc_array_access_using_var_index_test.cpp diff --git a/Android.mk b/Android.mk index b616654e..bc748e53 100644 --- a/Android.mk +++ b/Android.mk @@ -100,6 +100,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/debug_info_manager.cpp \ source/opt/def_use_manager.cpp \ source/opt/desc_sroa.cpp \ + source/opt/desc_sroa_util.cpp \ source/opt/dominator_analysis.cpp \ source/opt/dominator_tree.cpp \ source/opt/eliminate_dead_constant_pass.cpp \ @@ -157,6 +158,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/relax_float_ops_pass.cpp \ source/opt/remove_duplicates_pass.cpp \ source/opt/remove_unused_interface_variables_pass.cpp \ + source/opt/replace_desc_array_access_using_var_index.cpp \ source/opt/replace_invalid_opc.cpp \ source/opt/scalar_analysis.cpp \ source/opt/scalar_analysis_simplification.cpp \ diff --git a/BUILD.gn b/BUILD.gn index 4bf3f79e..309d5137 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -597,6 +597,8 @@ static_library("spvtools_opt") { "source/opt/def_use_manager.h", "source/opt/desc_sroa.cpp", "source/opt/desc_sroa.h", + "source/opt/desc_sroa_util.cpp", + "source/opt/desc_sroa_util.h", "source/opt/dominator_analysis.cpp", "source/opt/dominator_analysis.h", "source/opt/dominator_tree.cpp", @@ -716,6 +718,8 @@ static_library("spvtools_opt") { "source/opt/remove_duplicates_pass.h", "source/opt/remove_unused_interface_variables_pass.cpp", "source/opt/remove_unused_interface_variables_pass.h", + "source/opt/replace_desc_array_access_using_var_index.cpp", + "source/opt/replace_desc_array_access_using_var_index.h", "source/opt/replace_invalid_opc.cpp", "source/opt/replace_invalid_opc.h", "source/opt/scalar_analysis.cpp", diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 42eb6442..21059cbe 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -833,6 +833,13 @@ Optimizer::PassToken CreateFixStorageClassPass(); // inclusive. Optimizer::PassToken CreateGraphicsRobustAccessPass(); +// Create a pass to replace a descriptor access using variable index. +// This pass replaces every access using a variable index to array variable +// |desc| that has a DescriptorSet and Binding decorations with a constant +// element of the array. In order to replace the access using a variable index +// with the constant element, it uses a switch statement. +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass(); + // Create descriptor scalar replacement pass. // This pass replaces every array variable |desc| that has a DescriptorSet and // Binding decorations with a new variable for each element of the array. diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 63af5c1d..7d522fb5 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -39,6 +39,7 @@ set(SPIRV_TOOLS_OPT_SOURCES debug_info_manager.h def_use_manager.h desc_sroa.h + desc_sroa_util.h dominator_analysis.h dominator_tree.h eliminate_dead_constant_pass.h @@ -100,6 +101,7 @@ set(SPIRV_TOOLS_OPT_SOURCES relax_float_ops_pass.h remove_duplicates_pass.h remove_unused_interface_variables_pass.h + replace_desc_array_access_using_var_index.h replace_invalid_opc.h scalar_analysis.h scalar_analysis_nodes.h @@ -148,6 +150,7 @@ set(SPIRV_TOOLS_OPT_SOURCES debug_info_manager.cpp def_use_manager.cpp desc_sroa.cpp + desc_sroa_util.cpp dominator_analysis.cpp dominator_tree.cpp eliminate_dead_constant_pass.cpp @@ -205,6 +208,7 @@ set(SPIRV_TOOLS_OPT_SOURCES relax_float_ops_pass.cpp remove_duplicates_pass.cpp remove_unused_interface_variables_pass.cpp + replace_desc_array_access_using_var_index.cpp replace_invalid_opc.cpp scalar_analysis.cpp scalar_analysis_simplification.cpp diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp index 4bf026ef..2146c359 100644 --- a/source/opt/decoration_manager.cpp +++ b/source/opt/decoration_manager.cpp @@ -490,6 +490,14 @@ void DecorationManager::ForEachDecoration( }); } +bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) { + bool has_decoration = false; + ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) { + has_decoration = true; + }); + return has_decoration; +} + bool DecorationManager::FindDecoration( uint32_t id, uint32_t decoration, std::function f) { diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h index b753e6be..fe78f2ce 100644 --- a/source/opt/decoration_manager.h +++ b/source/opt/decoration_manager.h @@ -90,6 +90,10 @@ class DecorationManager { bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2, bool ignore_target) const; + // Returns whether a decoration instruction for |id| with decoration + // |decoration| exists or not. + bool HasDecoration(uint32_t id, uint32_t decoration); + // |f| is run on each decoration instruction for |id| with decoration // |decoration|. Processed are all decorations which target |id| either // directly or indirectly by Decoration Groups. diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index 5e950069..0b839370 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp @@ -14,6 +14,7 @@ #include "source/opt/desc_sroa.h" +#include "source/opt/desc_sroa_util.h" #include "source/util/string_utils.h" namespace spvtools { @@ -25,7 +26,7 @@ Pass::Status DescriptorScalarReplacement::Process() { std::vector vars_to_kill; for (Instruction& var : context()->types_values()) { - if (IsCandidate(&var)) { + if (descsroautil::IsDescriptorArray(context(), &var)) { modified = true; if (!ReplaceCandidate(&var)) { return Status::Failure; @@ -41,72 +42,6 @@ Pass::Status DescriptorScalarReplacement::Process() { return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); } -bool DescriptorScalarReplacement::IsCandidate(Instruction* var) { - if (var->opcode() != SpvOpVariable) { - return false; - } - - uint32_t ptr_type_id = var->type_id(); - Instruction* ptr_type_inst = - context()->get_def_use_mgr()->GetDef(ptr_type_id); - if (ptr_type_inst->opcode() != SpvOpTypePointer) { - return false; - } - - uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1); - Instruction* var_type_inst = - context()->get_def_use_mgr()->GetDef(var_type_id); - if (var_type_inst->opcode() != SpvOpTypeArray && - var_type_inst->opcode() != SpvOpTypeStruct) { - return false; - } - - // All structures with descriptor assignments must be replaced by variables, - // one for each of their members - with the exceptions of buffers. - if (IsTypeOfStructuredBuffer(var_type_inst)) { - return false; - } - - bool has_desc_set_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - var->result_id(), SpvDecorationDescriptorSet, - [&has_desc_set_decoration](const Instruction&) { - has_desc_set_decoration = true; - }); - if (!has_desc_set_decoration) { - return false; - } - - bool has_binding_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - var->result_id(), SpvDecorationBinding, - [&has_binding_decoration](const Instruction&) { - has_binding_decoration = true; - }); - if (!has_binding_decoration) { - return false; - } - - return true; -} - -bool DescriptorScalarReplacement::IsTypeOfStructuredBuffer( - const Instruction* type) const { - if (type->opcode() != SpvOpTypeStruct) { - return false; - } - - // All buffers have offset decorations for members of their structure types. - // This is how we distinguish it from a structure of descriptors. - bool has_offset_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - type->result_id(), SpvDecorationOffset, - [&has_offset_decoration](const Instruction&) { - has_offset_decoration = true; - }); - return has_offset_decoration; -} - bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) { std::vector access_chain_work_list; std::vector load_work_list; @@ -162,16 +97,15 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, return false; } - uint32_t idx_id = use->GetSingleWordInOperand(1); - const analysis::Constant* idx_const = - context()->get_constant_mgr()->FindDeclaredConstant(idx_id); - if (idx_const == nullptr) { + const analysis::Constant* const_index = + descsroautil::GetAccessChainIndexAsConst(context(), use); + if (const_index == nullptr) { context()->EmitErrorMessage("Variable cannot be replaced: invalid index", use); return false; } - uint32_t idx = idx_const->GetU32(); + uint32_t idx = const_index->GetU32(); uint32_t replacement_var = GetReplacementVariable(var, idx); if (use->NumInOperands() == 2) { @@ -208,39 +142,12 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, uint32_t idx) { auto replacement_vars = replacement_variables_.find(var); if (replacement_vars == replacement_variables_.end()) { - uint32_t ptr_type_id = var->type_id(); - Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); - assert(ptr_type_inst->opcode() == SpvOpTypePointer && - "Variable should be a pointer to an array or structure."); - uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); - Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id); - const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray; - const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct; - assert((is_array || is_struct) && - "Variable should be a pointer to an array or structure."); - - // For arrays, each array element should be replaced with a new replacement - // variable - if (is_array) { - uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1); - const analysis::Constant* array_len_const = - context()->get_constant_mgr()->FindDeclaredConstant(array_len_id); - assert(array_len_const != nullptr && "Array length must be a constant."); - uint32_t array_len = array_len_const->GetU32(); - - replacement_vars = replacement_variables_ - .insert({var, std::vector(array_len, 0)}) - .first; - } - // For structures, each member should be replaced with a new replacement - // variable - if (is_struct) { - const uint32_t num_members = pointee_type_inst->NumInOperands(); - replacement_vars = - replacement_variables_ - .insert({var, std::vector(num_members, 0)}) - .first; - } + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + replacement_vars = + replacement_variables_ + .insert({var, std::vector(number_of_elements, 0)}) + .first; } if (replacement_vars->second[idx] == 0) { @@ -377,7 +284,7 @@ uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType( // The number of bindings consumed by a structure is the sum of the bindings // used by its members. if (type_inst->opcode() == SpvOpTypeStruct && - !IsTypeOfStructuredBuffer(type_inst)) { + !descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) { uint32_t sum = 0; for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i)); diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index cd72fd30..70fd381a 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h @@ -46,10 +46,6 @@ class DescriptorScalarReplacement : public Pass { } private: - // Returns true if |var| is an OpVariable instruction that represents a - // descriptor array. These are the variables that we want to replace. - bool IsCandidate(Instruction* var); - // Replaces all references to |var| by new variables, one for each element of // the array |var|. The binding for the new variables corresponding to // element i will be the binding of |var| plus i. Returns true if successful. @@ -93,11 +89,6 @@ class DescriptorScalarReplacement : public Pass { // bindings used by its members. uint32_t GetNumBindingsUsedByType(uint32_t type_id); - // Returns true if |type| is a type that could be used for a structured buffer - // as opposed to a type that would be used for a structure of resource - // descriptors. - bool IsTypeOfStructuredBuffer(const Instruction* type) const; - // A map from an OpVariable instruction to the set of variables that will be // used to replace it. The entry |replacement_variables_[var][i]| is the id of // a variable that will be used in the place of the the ith element of the diff --git a/source/opt/desc_sroa_util.cpp b/source/opt/desc_sroa_util.cpp new file mode 100644 index 00000000..1954e2cc --- /dev/null +++ b/source/opt/desc_sroa_util.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2021 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/opt/desc_sroa_util.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpAccessChainInOperandIndexes = 1; + +// Returns the length of array type |type|. +uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) { + assert(type->opcode() == SpvOpTypeArray && "type must be array"); + uint32_t length_id = type->GetSingleWordInOperand(1); + const analysis::Constant* length_const = + context->get_constant_mgr()->FindDeclaredConstant(length_id); + assert(length_const != nullptr); + return length_const->GetU32(); +} + +} // namespace + +namespace descsroautil { + +bool IsDescriptorArray(IRContext* context, Instruction* var) { + if (var->opcode() != SpvOpVariable) { + return false; + } + + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + if (ptr_type_inst->opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id); + if (var_type_inst->opcode() != SpvOpTypeArray && + var_type_inst->opcode() != SpvOpTypeStruct) { + return false; + } + + // All structures with descriptor assignments must be replaced by variables, + // one for each of their members - with the exceptions of buffers. + if (IsTypeOfStructuredBuffer(context, var_type_inst)) { + return false; + } + + if (!context->get_decoration_mgr()->HasDecoration( + var->result_id(), SpvDecorationDescriptorSet)) { + return false; + } + + return context->get_decoration_mgr()->HasDecoration(var->result_id(), + SpvDecorationBinding); +} + +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) { + if (type->opcode() != SpvOpTypeStruct) { + return false; + } + + // All buffers have offset decorations for members of their structure types. + // This is how we distinguish it from a structure of descriptors. + return context->get_decoration_mgr()->HasDecoration(type->result_id(), + SpvDecorationOffset); +} + +const analysis::Constant* GetAccessChainIndexAsConst( + IRContext* context, Instruction* access_chain) { + if (access_chain->NumInOperands() <= 1) { + return nullptr; + } + uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain); + const analysis::Constant* idx_const = + context->get_constant_mgr()->FindDeclaredConstant(idx_id); + return idx_const; +} + +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) { + assert(access_chain->NumInOperands() > 1 && + "OpAccessChain does not have Indexes operand"); + return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes); +} + +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var) { + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == SpvOpTypePointer && + "Variable should be a pointer to an array or structure."); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = + context->get_def_use_mgr()->GetDef(pointee_type_id); + if (pointee_type_inst->opcode() == SpvOpTypeArray) { + return GetLengthOfArrayType(context, pointee_type_inst); + } + assert(pointee_type_inst->opcode() == SpvOpTypeStruct && + "Variable should be a pointer to an array or structure."); + return pointee_type_inst->NumInOperands(); +} + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools diff --git a/source/opt/desc_sroa_util.h b/source/opt/desc_sroa_util.h new file mode 100644 index 00000000..2f45c0c2 --- /dev/null +++ b/source/opt/desc_sroa_util.h @@ -0,0 +1,54 @@ +// Copyright (c) 2021 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_OPT_DESC_SROA_UTIL_H_ +#define SOURCE_OPT_DESC_SROA_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functions for the descriptor array SROA. +namespace descsroautil { + +// Returns true if |var| is an OpVariable instruction that represents a +// descriptor array. +bool IsDescriptorArray(IRContext* context, Instruction* var); + +// Returns true if |type| is a type that could be used for a structured buffer +// as opposed to a type that would be used for a structure of resource +// descriptors. +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type); + +// Returns the first index of the OpAccessChain instruction |access_chain| as +// a constant. Returns nullptr if it is not a constant. +const analysis::Constant* GetAccessChainIndexAsConst(IRContext* context, + Instruction* access_chain); + +// Returns the number of elements of an OpVariable instruction |var| whose type +// must be a pointer to an array or a struct. +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var); + +// Returns the first Indexes operand id of the OpAccessChain or +// OpInBoundsAccessChain instruction |access_chain|. The access chain must have +// at least 1 index. +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain); + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DESC_SROA_UTIL_H_ diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 990be2e4..e74db26f 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -320,6 +320,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateCombineAccessChainsPass()); } else if (pass_name == "convert-local-access-chains") { RegisterPass(CreateLocalAccessChainConvertPass()); + } else if (pass_name == "replace-desc-array-access-using-var-index") { + RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass()); } else if (pass_name == "descriptor-scalar-replacement") { RegisterPass(CreateDescriptorScalarReplacementPass()); } else if (pass_name == "eliminate-dead-code-aggressive") { @@ -958,6 +960,11 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass() { MakeUnique()); } +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() { + return MakeUnique( + MakeUnique()); +} + Optimizer::PassToken CreateDescriptorScalarReplacementPass() { return MakeUnique( MakeUnique()); diff --git a/source/opt/passes.h b/source/opt/passes.h index da837f2a..f3c30d57 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -66,6 +66,7 @@ #include "source/opt/relax_float_ops_pass.h" #include "source/opt/remove_duplicates_pass.h" #include "source/opt/remove_unused_interface_variables_pass.h" +#include "source/opt/replace_desc_array_access_using_var_index.h" #include "source/opt/replace_invalid_opc.h" #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" diff --git a/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp new file mode 100644 index 00000000..1082e679 --- /dev/null +++ b/source/opt/replace_desc_array_access_using_var_index.cpp @@ -0,0 +1,423 @@ +// Copyright (c) 2021 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/opt/replace_desc_array_access_using_var_index.h" + +#include "source/opt/desc_sroa_util.h" +#include "source/opt/ir_builder.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpAccessChainInOperandIndexes = 1; +const uint32_t kOpTypePointerInOperandType = 1; +const uint32_t kOpTypeArrayInOperandType = 0; +const uint32_t kOpTypeStructInOperandMember = 0; +IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping = + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + +uint32_t GetValueWithKeyExistenceCheck( + uint32_t key, const std::unordered_map& map) { + auto itr = map.find(key); + assert(itr != map.end() && "Key does not exist"); + return itr->second; +} + +} // namespace + +Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + if (descsroautil::IsDescriptorArray(context(), &var)) { + if (ReplaceVariableAccessesWithConstantElements(&var)) + status = Status::SuccessWithChange; + } + } + return status; +} + +bool ReplaceDescArrayAccessUsingVarIndex:: + ReplaceVariableAccessesWithConstantElements(Instruction* var) const { + std::vector work_list; + get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) { + switch (use->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + work_list.push_back(use); + break; + default: + break; + } + }); + + bool updated = false; + for (Instruction* access_chain : work_list) { + if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) == + nullptr) { + ReplaceAccessChain(var, access_chain); + updated = true; + } + } + // Note that we do not consider OpLoad and OpCompositeExtract because + // OpCompositeExtract always has constant literals for indices. + return updated; +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain( + Instruction* var, Instruction* access_chain) const { + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + assert(number_of_elements != 0 && "Number of element is 0"); + if (number_of_elements == 1) { + UseConstIndexForAccessChain(access_chain, 0); + get_def_use_mgr()->AnalyzeInstUse(access_chain); + return; + } + ReplaceUsersOfAccessChain(access_chain, number_of_elements); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain( + Instruction* access_chain, uint32_t number_of_elements) const { + std::vector final_users; + CollectRecursiveUsersWithConcreteType(access_chain, &final_users); + for (auto* inst : final_users) { + std::deque insts_to_be_cloned = + CollectRequiredImageInsts(inst); + ReplaceNonUniformAccessWithSwitchCase( + inst, access_chain, number_of_elements, insts_to_be_cloned); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const { + std::queue work_list; + work_list.push(access_chain); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + get_def_use_mgr()->ForEachUser( + inst_from_work_list, [this, final_users, &work_list](Instruction* use) { + // TODO: Support Boolean type as well. + if (!use->HasResultId() || IsConcreteType(use->type_id())) { + final_users->push_back(use); + } else { + work_list.push(use); + } + }); + } +} + +std::deque +ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts( + Instruction* user_of_image_insts) const { + std::unordered_set seen_inst_ids; + std::queue work_list; + + auto decision_to_include_operand = [this, &seen_inst_ids, + &work_list](uint32_t* idp) { + if (!seen_inst_ids.insert(*idp).second) return; + Instruction* operand = get_def_use_mgr()->GetDef(*idp); + if (context()->get_instr_block(operand) != nullptr && + HasImageOrImagePtrType(operand)) { + work_list.push(operand); + } + }; + + std::deque required_image_insts; + required_image_insts.push_front(user_of_image_insts); + user_of_image_insts->ForEachInId(decision_to_include_operand); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + required_image_insts.push_front(inst_from_work_list); + inst_from_work_list->ForEachInId(decision_to_include_operand); + } + return required_image_insts; +} + +bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType( + const Instruction* inst) const { + assert(inst != nullptr && inst->type_id() != 0 && "Invalid instruction"); + return IsImageOrImagePtrType(get_def_use_mgr()->GetDef(inst->type_id())); +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType( + const Instruction* type_inst) const { + if (type_inst->opcode() == SpvOpTypeImage || + type_inst->opcode() == SpvOpTypeSampler || + type_inst->opcode() == SpvOpTypeSampledImage) { + return true; + } + if (type_inst->opcode() == SpvOpTypePointer) { + Instruction* pointee_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType)); + return IsImageOrImagePtrType(pointee_type_inst); + } + if (type_inst->opcode() == SpvOpTypeArray) { + Instruction* element_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType)); + return IsImageOrImagePtrType(element_type_inst); + } + if (type_inst->opcode() != SpvOpTypeStruct) return false; + for (uint32_t in_operand_idx = kOpTypeStructInOperandMember; + in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) { + Instruction* member_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeStructInOperandMember)); + if (IsImageOrImagePtrType(member_type_inst)) return true; + } + return false; +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType( + uint32_t type_id) const { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + if (type_inst->opcode() == SpvOpTypeInt || + type_inst->opcode() == SpvOpTypeFloat) { + return true; + } + if (type_inst->opcode() == SpvOpTypeVector || + type_inst->opcode() == SpvOpTypeMatrix || + type_inst->opcode() == SpvOpTypeArray) { + return IsConcreteType(type_inst->GetSingleWordInOperand(0)); + } + if (type_inst->opcode() == SpvOpTypeStruct) { + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false; + } + return true; + } + return false; +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const { + auto* case_block = CreateNewBlock(); + AddConstElementAccessToCaseBlock(case_block, access_chain, element_index, + old_ids_to_new_ids); + CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned, + old_ids_to_new_ids); + AddBranchToBlock(case_block, branch_target_id); + UseNewIdsInBlock(case_block, *old_ids_to_new_ids); + return case_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const { + for (auto* inst_to_be_cloned : insts_to_be_cloned) { + if (inst_to_be_cloned == inst_to_skip_cloning) continue; + std::unique_ptr clone(inst_to_be_cloned->Clone(context())); + if (inst_to_be_cloned->HasResultId()) { + uint32_t new_id = context()->TakeNextId(); + clone->SetResultId(new_id); + (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id; + } + get_def_use_mgr()->AnalyzeInstDefUse(clone.get()); + context()->set_instr_block(clone.get(), block); + block->AddInstruction(std::move(clone)); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const { + for (auto block_itr = block->begin(); block_itr != block->end(); + ++block_itr) { + (&*block_itr)->ForEachInId([&old_ids_to_new_ids](uint32_t* idp) { + auto old_ids_to_new_ids_itr = old_ids_to_new_ids.find(*idp); + if (old_ids_to_new_ids_itr == old_ids_to_new_ids.end()) return; + *idp = old_ids_to_new_ids_itr->second; + }); + get_def_use_mgr()->AnalyzeInstUse(&*block_itr); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& insts_to_be_cloned) const { + // Create merge block and add terminator + auto* block = context()->get_instr_block(access_chain_final_user); + auto* merge_block = SeparateInstructionsIntoNewBlock( + block, access_chain_final_user->NextNode()); + + auto* function = block->GetParent(); + + // Add case blocks + std::vector phi_operands; + std::vector case_block_ids; + for (uint32_t idx = 0; idx < number_of_elements; ++idx) { + std::unordered_map old_ids_to_new_ids_for_cloned_insts; + std::unique_ptr case_block(CreateCaseBlock( + access_chain, idx, insts_to_be_cloned, merge_block->id(), + &old_ids_to_new_ids_for_cloned_insts)); + case_block_ids.push_back(case_block->id()); + function->InsertBasicBlockBefore(std::move(case_block), merge_block); + + // Keep the operand for OpPhi + if (!access_chain_final_user->HasResultId()) continue; + uint32_t phi_operand = + GetValueWithKeyExistenceCheck(access_chain_final_user->result_id(), + old_ids_to_new_ids_for_cloned_insts); + phi_operands.push_back(phi_operand); + } + + // Create default block + std::unique_ptr default_block( + CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands, + merge_block->id())); + uint32_t default_block_id = default_block->id(); + function->InsertBasicBlockBefore(std::move(default_block), merge_block); + + // Create OpSwitch + uint32_t access_chain_index_var_id = + descsroautil::GetFirstIndexOfAccessChain(access_chain); + AddSwitchForAccessChain(block, access_chain_index_var_id, default_block_id, + merge_block->id(), case_block_ids); + + // Create phi instructions + if (!phi_operands.empty()) { + uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands, + case_block_ids, default_block_id); + context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id); + } + + // Replace OpPhi incoming block operand that uses |block| with |merge_block| + ReplacePhiIncomingBlock(block->id(), merge_block->id()); +} + +BasicBlock* +ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const { + auto separation_begin = block->begin(); + while (separation_begin != block->end() && + &*separation_begin != separation_begin_inst) { + ++separation_begin; + } + return block->SplitBasicBlock(context(), context()->TakeNextId(), + separation_begin); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const { + auto* new_block = new BasicBlock(std::unique_ptr( + new Instruction(context(), SpvOpLabel, 0, context()->TakeNextId(), {}))); + get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst()); + context()->set_instr_block(new_block->GetLabelInst(), new_block); + return new_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain( + Instruction* access_chain, uint32_t const_element_idx) const { + uint32_t const_element_idx_id = + context()->get_constant_mgr()->GetUIntConst(const_element_idx); + access_chain->SetInOperand(kOpAccessChainInOperandIndexes, + {const_element_idx_id}); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const { + std::unique_ptr access_clone(access_chain->Clone(context())); + UseConstIndexForAccessChain(access_clone.get(), const_element_idx); + + uint32_t new_access_id = context()->TakeNextId(); + (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id; + access_clone->SetResultId(new_access_id); + get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get()); + + context()->set_instr_block(access_clone.get(), case_block); + case_block->AddInstruction(std::move(access_clone)); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock( + BasicBlock* parent_block, uint32_t branch_destination) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + builder.AddBranch(branch_destination); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock( + bool null_const_for_phi_is_needed, std::vector* phi_operands, + uint32_t merge_block_id) const { + auto* default_block = CreateNewBlock(); + AddBranchToBlock(default_block, merge_block_id); + if (!null_const_for_phi_is_needed) return default_block; + + // Create null value for OpPhi + Instruction* inst = context()->get_def_use_mgr()->GetDef((*phi_operands)[0]); + auto* null_const_inst = GetConstNull(inst->type_id()); + phi_operands->push_back(null_const_inst->result_id()); + return default_block; +} + +Instruction* ReplaceDescArrayAccessUsingVarIndex::GetConstNull( + uint32_t type_id) const { + assert(type_id != 0 && "Result type is expected"); + auto* type = context()->get_type_mgr()->GetType(type_id); + auto* null_const = context()->get_constant_mgr()->GetConstant(type, {}); + return context()->get_constant_mgr()->GetDefiningInstruction(null_const); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + std::vector> cases; + for (uint32_t i = 0; i < static_cast(case_block_ids.size()); ++i) { + cases.emplace_back(Operand::OperandData{i}, case_block_ids[i]); + } + builder.AddSwitch(access_chain_index_var_id, default_id, cases, merge_id); +} + +uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction( + BasicBlock* parent_block, const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const { + std::vector incomings; + assert(case_block_ids.size() + 1 == phi_operands.size() && + "Number of Phi operands must be exactly 1 bigger than the one of case " + "blocks"); + for (size_t i = 0; i < case_block_ids.size(); ++i) { + incomings.push_back(phi_operands[i]); + incomings.push_back(case_block_ids[i]); + } + incomings.push_back(phi_operands.back()); + incomings.push_back(default_block_id); + + InstructionBuilder builder{context(), &*parent_block->begin(), + kAnalysisDefUseAndInstrToBlockMapping}; + uint32_t phi_result_type_id = + context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id(); + auto* phi = builder.AddPhi(phi_result_type_id, incomings); + return phi->result_id(); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplacePhiIncomingBlock( + uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const { + context()->ReplaceAllUsesWithPredicate( + old_incoming_block_id, new_incoming_block_id, + [](Instruction* use) { return use->opcode() == SpvOpPhi; }); +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h new file mode 100644 index 00000000..e18222c8 --- /dev/null +++ b/source/opt/replace_desc_array_access_using_var_index.h @@ -0,0 +1,204 @@ +// Copyright (c) 2021 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_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ +#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class ReplaceDescArrayAccessUsingVarIndex : public Pass { + public: + ReplaceDescArrayAccessUsingVarIndex() {} + + const char* name() const override { + return "replace-desc-array-access-using-var-index"; + } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces all acceses to |var| using variable indices with constant + // elements of the array |var|. Creates switch-case statements to determine + // the value of the variable index for all the possible cases. Returns + // whether replacement is done or not. + bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const; + + // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that + // uses the descriptor variable |var| with the OpAccessChain or + // OpInBoundsAccessChain instruction with a constant Indexes operand. + void ReplaceAccessChain(Instruction* var, Instruction* use) const; + + // Updates the first Indexes operand of the OpAccessChain or + // OpInBoundsAccessChain instruction |access_chain| to let it use a constant + // index |const_element_idx|. + void UseConstIndexForAccessChain(Instruction* access_chain, + uint32_t const_element_idx) const; + + // Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| that accesses an array descriptor variable using variable + // indices with constant elements. |number_of_elements| is the number + // of array elements. + void ReplaceUsersOfAccessChain(Instruction* access_chain, + uint32_t number_of_elements) const; + + // Puts all the recursive users of |access_chain| with concrete result types + // or the ones without result it in |final_users|. + void CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const; + + // Recursively collects the operands of |user_of_image_insts| (and operands + // of the operands) whose result types are images/samplers or pointers/array/ + // struct of them and returns them. + std::deque CollectRequiredImageInsts( + Instruction* user_of_image_insts) const; + + // Returns whether result type of |inst| is an image/sampler/pointer of image + // or sampler or not. + bool HasImageOrImagePtrType(const Instruction* inst) const; + + // Returns whether |type_inst| is an image/sampler or pointer/array/struct of + // image or sampler or not. + bool IsImageOrImagePtrType(const Instruction* type_inst) const; + + // Returns whether the type with |type_id| is a concrete type or not. + bool IsConcreteType(uint32_t type_id) const; + + // Replaces the non-uniform access to a descriptor variable + // |access_chain_final_user| with OpSwitch instruction and case blocks. Each + // case block will contain a clone of |access_chain| and clones of + // |non_uniform_accesses_to_clone| that are recursively used by + // |access_chain_final_user|. The clone of |access_chain| (or + // OpInBoundsAccessChain) will have a constant index for its first index. The + // OpSwitch instruction will have the cases for the variable index of + // |access_chain| from 0 to |number_of_elements| - 1. + void ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& non_uniform_accesses_to_clone) const; + + // Creates and returns a new basic block that contains all instructions of + // |block| after |separation_begin_inst|. The new basic block is added to the + // function in this method. + BasicBlock* SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const; + + // Creates and returns a new block. + BasicBlock* CreateNewBlock() const; + + // Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain + // instruction |access_chain|. + uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const; + + // Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| to |case_block|. The clone of |access_chain| will use + // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the + // mapping from the result id of |access_chain| to the result of its clone. + void AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const; + + // Clones all instructions in |insts_to_be_cloned| and put them to |block|. + // |old_ids_to_new_ids| keeps the mapping from the result id of each + // instruction of |insts_to_be_cloned| to the result of their clones. + void CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const; + + // Adds OpBranch to |branch_destination| at the end of |parent_block|. + void AddBranchToBlock(BasicBlock* parent_block, + uint32_t branch_destination) const; + + // Replaces in-operands of all instructions in the basic block |block| using + // |old_ids_to_new_ids|. It conducts the replacement only if the in-operand + // id is a key of |old_ids_to_new_ids|. + void UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const; + + // Creates a case block for |element_index| case. It adds clones of + // |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as + // its first index. The termination instruction of the created case block will + // be a branch to |branch_target_id|. Puts old ids to new ids map for the + // cloned instructions in |old_ids_to_new_ids|. + BasicBlock* CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const; + + // Creates a default block for switch-case statement that has only a single + // instruction OpBranch whose target is a basic block with |merge_block_id|. + // If |null_const_for_phi_is_needed| is true, gets or creates a default null + // constant value for a phi instruction whose operands are |phi_operands| and + // puts it in |phi_operands|. + BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed, + std::vector* phi_operands, + uint32_t merge_block_id) const; + + // Creates and adds an OpSwitch used for the selection of OpAccessChain whose + // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be + // added at the end of |parent_block|. It will jump to |default_id| for the + // default case and jumps to one of case blocks whoes ids are |case_block_ids| + // if |access_chain_index_var_id| matches the case number. |merge_id| is the + // merge block id. + void AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const; + + // Creates a phi instruction with |phi_operands| as values and + // |case_block_ids| and |default_block_id| as incoming blocks. The size of + // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|. + // The last element of |phi_operands| will be used for |default_block_id|. It + // adds the phi instruction to the beginning of |parent_block|. + uint32_t CreatePhiInstruction(BasicBlock* parent_block, + const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const; + + // Replaces the incoming block operand of OpPhi instructions with + // |new_incoming_block_id| if the incoming block operand is + // |old_incoming_block_id|. + void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id, + uint32_t new_incoming_block_id) const; + + // Create an OpConstantNull instruction whose result type id is |type_id|. + Instruction* GetConstNull(uint32_t type_id) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 11299755..bc44e8d6 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -85,6 +85,7 @@ add_spvtools_unittest(TARGET opt remove_unused_interface_variables_test.cpp register_liveness.cpp relax_float_ops_test.cpp + replace_desc_array_access_using_var_index_test.cpp replace_invalid_opc_test.cpp scalar_analysis.cpp scalar_replacement_test.cpp diff --git a/test/opt/replace_desc_array_access_using_var_index_test.cpp b/test/opt/replace_desc_array_access_using_var_index_test.cpp new file mode 100644 index 00000000..ca625812 --- /dev/null +++ b/test/opt/replace_desc_array_access_using_var_index_test.cpp @@ -0,0 +1,411 @@ +// Copyright (c) 2021 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 + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ReplaceDescArrayAccessUsingVarIndexTest = PassTest<::testing::Test>; + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArray) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 + %bool = OpTypeBool +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_3 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_2 = OpConstant %uint 2 + %66 = OpConstantNull %v4float + +; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float + + %psmain = OpFunction %void None %21 + %39 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %37 = OpIEqual %bool %30 %uint_2 + OpSelectionMerge %38 None + OpBranchConditional %37 %28 %40 + +; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID +; CHECK: OpSelectionMerge [[cond_branch_merge:%\w+]] None +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[bb_cond_br:%\w+]] + + %28 = OpLabel + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case2]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel + + %33 = OpLoad %type_sampler %Sampler0 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %33 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[case2]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_2 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case0]] [[value1]] [[case1]] [[value2]] [[case2]] [[null_value]] [[default]] + + OpBranch %38 + %40 = OpLabel + OpBranch %38 + %38 = OpLabel + %41 = OpPhi %v4float %36 %28 %29 %40 + +; CHECK: OpBranch [[cond_branch_merge]] +; CHECK: [[bb_cond_br]] = OpLabel +; CHECK: OpBranch [[cond_branch_merge]] +; CHECK: [[cond_branch_merge]] = OpLabel +; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[phi0]] [[merge]] {{%\w+}} [[bb_cond_br]] +; CHECK: OpStore {{%\w+}} [[phi1]] + + OpStore %out_var_SV_TARGET %41 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayAndSamplerArray) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 +%type_sampler = OpTypeSampler + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2 +%_ptr_UniformConstant__arr_type_sampler_uint_2 = OpTypePointer UniformConstant %_arr_type_sampler_uint_2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 +%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_2 UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %66 = OpConstantNull %v4float + %psmain = OpFunction %void None %21 + %28 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float + +; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel + + %33 = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %30 + %37 = OpLoad %type_sampler %33 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %37 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + +; SPIR-V instructions to be replaced (will be killed by ADCE) +; CHECK: OpSelectionMerge +; CHECK: OpSwitch + +; CHECK: OpSelectionMerge [[merge_sampler:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_sampler:%\w+]] 0 [[case_sampler0:%\w+]] 1 [[case_sampler1:%\w+]] + +; CHECK: [[case_sampler0]] = OpLabel +; CHECK: OpSelectionMerge [[merge_texture0:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]] +; CHECK: [[case_texture0]] = OpLabel +; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0 +; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]] +; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]] +; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s0]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]] +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[case_texture1]] = OpLabel +; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0 +; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]] +; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]] +; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s0]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]] +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[default_texture]] = OpLabel +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[merge_texture0]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]] +; CHECK: OpBranch [[merge_sampler]] + +; CHECK: [[case_sampler1]] = OpLabel +; CHECK: OpSelectionMerge [[merge_texture1:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]] +; CHECK: [[case_texture0]] = OpLabel +; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1 +; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]] +; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]] +; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s1]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]] +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[case_texture1]] = OpLabel +; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1 +; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]] +; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]] +; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s1]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]] +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[default_texture]] = OpLabel +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[merge_texture1]] = OpLabel +; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]] + +; CHECK: [[default_sampler]] = OpLabel +; CHECK: OpBranch [[merge_sampler]] +; CHECK: [[merge_sampler]] = OpLabel +; CHECK: OpPhi %v4float [[phi0]] [[merge_texture0]] [[phi1]] [[merge_texture1]] [[null_value]] [[default_sampler]] +; CHECK: OpStore + + OpStore %out_var_SV_TARGET %36 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayWithSingleElement) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_1 = OpTypeArray %type_2d_image %uint_1 +%_ptr_UniformConstant__arr_type_2d_image_uint_1 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_1 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_1 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_2 = OpConstant %uint 2 + %66 = OpConstantNull %v4float + %psmain = OpFunction %void None %21 + %28 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK-NOT: OpAccessChain +; CHECK-NOT: OpSwitch +; CHECK-NOT: OpPhi + + %33 = OpLoad %type_sampler %Sampler0 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %33 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + + OpStore %out_var_SV_TARGET %36 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index d5036fc3..04f81b8c 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -163,6 +163,11 @@ Options (in lexicographical order):)", around known issues with some Vulkan drivers for initialize variables.)"); printf(R"( + --replace-desc-array-access-using-var-index + Replaces accesses to descriptor arrays based on a variable index + with a switch that has a case for every possible value of the + index.)"); + printf(R"( --descriptor-scalar-replacement Replaces every array variable |desc| that has a DescriptorSet and Binding decorations with a new variable for each element of -- cgit v1.2.3 From 3291b6951e78baf625c920e2681bc86a96bcffdc Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 28 Oct 2021 10:02:57 -0400 Subject: Do not fold snegate feeding sdiv. (#4600) When the variable value is INT_MIN, we cannot fold the negate into the divide, so we have to turn off that folding rule. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4487. --- source/opt/folding_rules.cpp | 18 ++++-------------- test/opt/fold_test.cpp | 24 ++++++++---------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 886c03fd..4904f186 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -968,25 +968,17 @@ FoldingRule MergeDivMulArithmetic() { FoldingRule MergeDivNegateArithmetic() { return [](IRContext* context, Instruction* inst, const std::vector& constants) { - assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv); + assert(inst->opcode() == SpvOpFDiv); analysis::ConstantManager* const_mgr = context->get_constant_mgr(); - const analysis::Type* type = - context->get_type_mgr()->GetType(inst->type_id()); - bool uses_float = HasFloatingPoint(type); - if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; - - uint32_t width = ElementWidth(type); - if (width != 32 && width != 64) return false; + if (!inst->IsFloatingPointFoldingAllowed()) return false; const analysis::Constant* const_input1 = ConstInput(constants); if (!const_input1) return false; Instruction* other_inst = NonConstInput(context, constants[0], inst); - if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) - return false; + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; bool first_is_variable = constants[0] == nullptr; - if (other_inst->opcode() == SpvOpFNegate || - other_inst->opcode() == SpvOpSNegate) { + if (other_inst->opcode() == SpvOpFNegate) { uint32_t neg_id = NegateConstant(const_mgr, const_input1); if (first_is_variable) { @@ -2589,8 +2581,6 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpPhi].push_back(RedundantPhi()); - rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic()); - rules_[SpvOpSNegate].push_back(MergeNegateArithmetic()); rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic()); rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic()); diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 95241e12..0487e78c 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -5865,15 +5865,11 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "OpReturn\n" + "OpFunctionEnd\n", 4, false), - // Test case 11: merge sdiv of snegate - // (-x) / 2 = x / -2 + // Test case 11: Do not merge sdiv of snegate. If %2 is INT_MIN, then the + // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv. + // Specifically, (-INT_MIN) / 2 != INT_MIN / -2. InstructionFoldingCase( Header() + - "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + - "; CHECK: OpConstant [[int]] -2147483648\n" + - "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + - "; CHECK: %4 = OpSDiv [[int]] [[ld]] [[int_n2]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_int Function\n" + @@ -5882,16 +5878,12 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%4 = OpSDiv %int %3 %int_2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 4, true), - // Test case 12: merge sdiv of snegate - // 2 / (-x) = -2 / x + 4, false), + // Test case 12: Do not merge sdiv of snegate. If %2 is INT_MIN, then the + // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv. + // Specifically, 2 / (-INT_MIN) != -2 / INT_MIN. InstructionFoldingCase( Header() + - "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + - "; CHECK: OpConstant [[int]] -2147483648\n" + - "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + - "; CHECK: %4 = OpSDiv [[int]] [[int_n2]] [[ld]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_int Function\n" + @@ -5900,7 +5892,7 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%4 = OpSDiv %int %int_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", - 4, true), + 4, false), // Test case 13: Don't merge // (x / {null}) / {null} InstructionFoldingCase( -- cgit v1.2.3 From b2ba019bf6fb50a34b40cb0c6abcf90d0e86954a Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 28 Oct 2021 10:25:37 -0400 Subject: Delete decorations before replaces uses in dead branch elim (#4598) If we do not delete the decoration before all ReplaceAllUses, the decorations will be transferred to the new id. This can cause problems. Fixes #4442. --- source/opt/dead_branch_elim_pass.cpp | 1 + test/opt/dead_branch_elim_test.cpp | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp index 0054f576..1913414a 100644 --- a/source/opt/dead_branch_elim_pass.cpp +++ b/source/opt/dead_branch_elim_pass.cpp @@ -346,6 +346,7 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks( if (operands.size() == 4) { // First input data operands is at index 2. uint32_t replId = operands[2u].words[0]; + context()->KillNamesAndDecorates(inst->result_id()); context()->ReplaceAllUsesWith(inst->result_id(), replId); iter = context()->KillInst(&*inst); } else { diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp index f89befb4..9c49f49f 100644 --- a/test/opt/dead_branch_elim_test.cpp +++ b/test/opt/dead_branch_elim_test.cpp @@ -3403,6 +3403,45 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(DeadBranchElimTest, DontTransferDecorations) { + // When replacing %4 with %14, we don't want %14 to inherit %4's decorations. + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} RelaxedPrecision +; CHECK: [[div:%\w+]] = OpFDiv +; CHECK: {{%\w+}} = OpCopyObject %float [[div]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpString "STEVEN" + OpDecorate %4 RelaxedPrecision + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %float_1 = OpConstant %float 1 + %uint_0 = OpConstant %uint 0 + %10 = OpTypeFunction %void + %2 = OpFunction %void None %10 + %11 = OpLabel + OpSelectionMerge %12 None + OpSwitch %uint_0 %13 + %13 = OpLabel + %14 = OpFDiv %float %float_1 %float_1 + OpLine %3 0 0 + OpBranch %12 + %15 = OpLabel + OpBranch %12 + %12 = OpLabel + %4 = OpPhi %float %float_1 %15 %14 %13 + %16 = OpCopyObject %float %4 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow -- cgit v1.2.3 From 7c5b17d379f2b2eb218c80df8aa8881b468ba802 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 28 Oct 2021 11:54:37 -0400 Subject: Update passes to handle function declarations (#4599) Spirv-opt has not had to handle module with function declarations. This lead many passes to assume that every function has a body. This is not always true. This commit will modify a number of passes to handle function declarations. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4443 --- source/opt/ccp_pass.cpp | 4 +++ source/opt/combine_access_chains.cpp | 4 +++ source/opt/copy_prop_arrays.cpp | 4 +++ source/opt/dead_branch_elim_pass.cpp | 4 +++ source/opt/function.h | 3 +++ source/opt/loop_unroller.cpp | 4 +++ source/opt/redundancy_elimination.cpp | 4 +++ source/opt/scalar_replacement_pass.cpp | 4 +++ source/opt/simplification_pass.cpp | 4 +++ source/opt/ssa_rewrite_pass.cpp | 3 +++ test/opt/ccp_test.cpp | 26 ++++++++++++++++++ test/opt/combine_access_chains_test.cpp | 26 ++++++++++++++++++ test/opt/copy_prop_array_test.cpp | 25 +++++++++++++++++ test/opt/dead_branch_elim_test.cpp | 26 ++++++++++++++++++ test/opt/local_ssa_elim_test.cpp | 26 ++++++++++++++++++ test/opt/loop_optimizations/unroll_assumptions.cpp | 31 ++++++++++++++++++++++ test/opt/redundancy_elimination_test.cpp | 28 ++++++++++++++++++- test/opt/scalar_replacement_test.cpp | 26 ++++++++++++++++++ test/opt/simplification_test.cpp | 25 +++++++++++++++++ 19 files changed, 276 insertions(+), 1 deletion(-) diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp index d84f13f5..8b896d50 100644 --- a/source/opt/ccp_pass.cpp +++ b/source/opt/ccp_pass.cpp @@ -291,6 +291,10 @@ bool CCPPass::ReplaceValues() { } bool CCPPass::PropagateConstants(Function* fp) { + if (fp->IsDeclaration()) { + return false; + } + // Mark function parameters as varying. fp->ForEachParam([this](const Instruction* inst) { values_[inst->result_id()] = kVaryingSSAId; diff --git a/source/opt/combine_access_chains.cpp b/source/opt/combine_access_chains.cpp index facfc24b..142897a2 100644 --- a/source/opt/combine_access_chains.cpp +++ b/source/opt/combine_access_chains.cpp @@ -34,6 +34,10 @@ Pass::Status CombineAccessChains::Process() { } bool CombineAccessChains::ProcessFunction(Function& function) { + if (function.IsDeclaration()) { + return false; + } + bool modified = false; cfg()->ForEachBlockInReversePostOrder( diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp index f505d8a5..62ed5e77 100644 --- a/source/opt/copy_prop_arrays.cpp +++ b/source/opt/copy_prop_arrays.cpp @@ -40,6 +40,10 @@ bool IsDebugDeclareOrValue(Instruction* di) { Pass::Status CopyPropagateArrays::Process() { bool modified = false; for (Function& function : *get_module()) { + if (function.IsDeclaration()) { + continue; + } + BasicBlock* entry_bb = &*function.begin(); for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable; diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp index 1913414a..356dbcb3 100644 --- a/source/opt/dead_branch_elim_pass.cpp +++ b/source/opt/dead_branch_elim_pass.cpp @@ -420,6 +420,10 @@ bool DeadBranchElimPass::EraseDeadBlocks( } bool DeadBranchElimPass::EliminateDeadBranches(Function* func) { + if (func->IsDeclaration()) { + return false; + } + bool modified = false; std::unordered_set live_blocks; modified |= MarkLiveBlocks(func, &live_blocks); diff --git a/source/opt/function.h b/source/opt/function.h index 9e1c7274..917bf584 100644 --- a/source/opt/function.h +++ b/source/opt/function.h @@ -177,6 +177,9 @@ class Function { // debuggers. void Dump() const; + // Returns true is a function declaration and not a function definition. + bool IsDeclaration() { return begin() == end(); } + private: // The OpFunction instruction that begins the definition of this function. std::unique_ptr def_inst_; diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index bf622500..aff191fe 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp @@ -1102,6 +1102,10 @@ void LoopUtils::Finalize() { Pass::Status LoopUnroller::Process() { bool changed = false; for (Function& f : *context()->module()) { + if (f.IsDeclaration()) { + continue; + } + LoopDescriptor* LD = context()->GetLoopDescriptor(&f); for (Loop& loop : *LD) { LoopUtils loop_utils{context(), &loop}; diff --git a/source/opt/redundancy_elimination.cpp b/source/opt/redundancy_elimination.cpp index 362e54dc..398225bb 100644 --- a/source/opt/redundancy_elimination.cpp +++ b/source/opt/redundancy_elimination.cpp @@ -24,6 +24,10 @@ Pass::Status RedundancyEliminationPass::Process() { ValueNumberTable vnTable(context()); for (auto& func : *get_module()) { + if (func.IsDeclaration()) { + continue; + } + // Build the dominator tree for this function. It is how the code is // traversed. DominatorTree& dom_tree = diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index 8bc04991..4d6a7aad 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -35,6 +35,10 @@ namespace opt { Pass::Status ScalarReplacementPass::Process() { Status status = Status::SuccessWithoutChange; for (auto& f : *get_module()) { + if (f.IsDeclaration()) { + continue; + } + Status functionStatus = ProcessFunction(&f); if (functionStatus == Status::Failure) return functionStatus; diff --git a/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp index 319ceecf..43ec15f8 100644 --- a/source/opt/simplification_pass.cpp +++ b/source/opt/simplification_pass.cpp @@ -45,6 +45,10 @@ void SimplificationPass::AddNewOperands( } bool SimplificationPass::SimplifyFunction(Function* function) { + if (function->IsDeclaration()) { + return false; + } + bool modified = false; // Phase 1: Traverse all instructions in dominance order. // The second phase will only be on the instructions whose inputs have changed diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp index 81770d77..29ab6123 100644 --- a/source/opt/ssa_rewrite_pass.cpp +++ b/source/opt/ssa_rewrite_pass.cpp @@ -753,6 +753,9 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) { Pass::Status SSARewritePass::Process() { Status status = Status::SuccessWithoutChange; for (auto& fn : *get_module()) { + if (fn.IsDeclaration()) { + continue; + } status = CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn)); // Kill DebugDeclares for target variables. diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp index ef734353..212e0514 100644 --- a/test/opt/ccp_test.cpp +++ b/test/opt/ccp_test.cpp @@ -1208,6 +1208,32 @@ TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) { auto result = SinglePassRunAndMatch(text, true); EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); } + +TEST_F(CCPTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp index aed14c9d..5be3ba63 100644 --- a/test/opt/combine_access_chains_test.cpp +++ b/test/opt/combine_access_chains_test.cpp @@ -768,6 +768,32 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(CombineAccessChainsTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp index 72bc7f6f..a4599f0f 100644 --- a/test/opt/copy_prop_array_test.cpp +++ b/test/opt/copy_prop_array_test.cpp @@ -1814,6 +1814,31 @@ OpFunctionEnd SinglePassRunAndMatch(before, false); } +TEST_F(CopyPropArrayPassTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp index 9c49f49f..9c1ef2a3 100644 --- a/test/opt/dead_branch_elim_test.cpp +++ b/test/opt/dead_branch_elim_test.cpp @@ -3442,6 +3442,32 @@ TEST_F(DeadBranchElimTest, DontTransferDecorations) { SinglePassRunAndMatch(text, true); } +TEST_F(DeadBranchElimTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp index ae98649f..4b7542fe 100644 --- a/test/opt/local_ssa_elim_test.cpp +++ b/test/opt/local_ssa_elim_test.cpp @@ -4225,6 +4225,32 @@ TEST_F(LocalSSAElimTest, PointerVariables) { SinglePassRunAndMatch(text, true); } +TEST_F(LocalSSAElimTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // No optimization in the presence of diff --git a/test/opt/loop_optimizations/unroll_assumptions.cpp b/test/opt/loop_optimizations/unroll_assumptions.cpp index 0f933021..159e4a14 100644 --- a/test/opt/loop_optimizations/unroll_assumptions.cpp +++ b/test/opt/loop_optimizations/unroll_assumptions.cpp @@ -42,6 +42,10 @@ class PartialUnrollerTestPass : public Pass { Status Process() override { bool changed = false; for (Function& f : *context()->module()) { + if (f.IsDeclaration()) { + continue; + } + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); for (auto& loop : loop_descriptor) { LoopUtils loop_utils{context(), &loop}; @@ -1510,6 +1514,33 @@ OpFunctionEnd SinglePassRunAndCheck>(text, text, false); } +TEST_F(PassClassTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp index 474f4661..28eda73e 100644 --- a/test/opt/redundancy_elimination_test.cpp +++ b/test/opt/redundancy_elimination_test.cpp @@ -335,6 +335,32 @@ TEST_F(RedundancyEliminationTest, OpenCLDebugInfo100) { SinglePassRunAndMatch(text, false); } +TEST_F(RedundancyEliminationTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt -} // namespace spvtools +} // namespace spvtools \ No newline at end of file diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp index d99399ad..8cb888c9 100644 --- a/test/opt/scalar_replacement_test.cpp +++ b/test/opt/scalar_replacement_test.cpp @@ -2237,6 +2237,32 @@ OpFunctionEnd SinglePassRunAndMatch(text, false); } +TEST_F(ScalarReplacementTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/simplification_test.cpp b/test/opt/simplification_test.cpp index 7a9696ea..7727f567 100644 --- a/test/opt/simplification_test.cpp +++ b/test/opt/simplification_test.cpp @@ -360,6 +360,31 @@ OpFunctionEnd SinglePassRunAndMatch(spirv, true); } +TEST_F(SimplificationTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From 2feb7074d412c0679dc73403f93a9778838200da Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 28 Oct 2021 15:57:36 -0400 Subject: Avoid confusing short-circuiting (#3404) When checking the OpBranchConditional for selection headers, we intend to register both the true and false targets. Short circuiting was getting in the way. Co-authored-by: Steven Perron --- source/val/validate_cfg.cpp | 2 +- test/val/val_cfg_test.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index f08ab19f..c8e26fc1 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -660,7 +660,7 @@ spv_result_t ValidateStructuredSelections( // was missing a merge instruction and both labels hadn't been seen // previously. const bool both_unseen = - seen.insert(true_label).second && seen.insert(false_label).second; + seen.insert(true_label).second & seen.insert(false_label).second; if (!merge && both_unseen) { return _.diag(SPV_ERROR_INVALID_CFG, terminator) << "Selection must be structured"; diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index c2120b56..311cfa77 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -4229,6 +4229,67 @@ OpFunctionEnd ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateCFG, StructuredSelections_RegisterBothTrueAndFalse) { + // In this test, we try to make a case where the false branches + // to %20 and %60 from blocks %10 and %50 must be registered + // during the validity check for sturctured selections. + // However, an error is caught earlier in the flow, that the + // branches from %100 to %20 and %60 violate dominance. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %bool = OpTypeBool + %cond = OpUndef %bool + + %main = OpFunction %void None %void_fn + + %1 = OpLabel + OpSelectionMerge %999 None + OpBranchConditional %cond %10 %100 + + %10 = OpLabel + OpSelectionMerge %30 None ; force registration of %30 + OpBranchConditional %cond %30 %20 ; %20 should be registered too + + %20 = OpLabel + OpBranch %30 + + %30 = OpLabel ; merge for first if + OpBranch %50 + + + %50 = OpLabel + OpSelectionMerge %70 None ; force registration of %70 + OpBranchConditional %cond %70 %60 ; %60 should be registered + + %60 = OpLabel + OpBranch %70 + + %70 = OpLabel ; merge for second if + OpBranch %999 + + %100 = OpLabel + OpBranchConditional %cond %20 %60 ; should require a merge + + %999 = OpLabel + OpReturn + + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The selection construct with the selection header " + "8[%8] does not dominate the merge block 10[%10]\n")); +} + TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) { const std::string text = R"( OpCapability Shader -- cgit v1.2.3 From 791f5b463aa46662a5ad81db72f78a69211635f1 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Thu, 28 Oct 2021 16:27:56 -0400 Subject: Only validate workgroup layout for explicit workgroup memory (#4542) Fixes #4537 * Only validate the layout of workgroup variables if the WorkgroupMemoryExplicitLayoutKHR capability is declared --- source/val/validate_decorations.cpp | 4 +++- test/val/val_memory_test.cpp | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index b7e38a3f..3cdb471c 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -997,7 +997,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool phys_storage_buffer = storageClass == SpvStorageClassPhysicalStorageBufferEXT; - const bool workgroup = storageClass == SpvStorageClassWorkgroup; + const bool workgroup = + storageClass == SpvStorageClassWorkgroup && + vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR); if (uniform || push_constant || storage_buffer || phys_storage_buffer || workgroup) { const auto ptrInst = vstate.FindDef(words[1]); diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 616a88f6..f17133cc 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -4372,6 +4372,29 @@ OpFunctionEnd HasSubstr("Cannot load a runtime-sized array")); } +TEST_F(ValidateMemory, Pre1p4WorkgroupMemoryBadLayoutOk) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%bool = OpTypeBool +%struct = OpTypeStruct %bool +%ptr = OpTypePointer Workgroup %struct +%var = OpVariable %ptr Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From bd5bf754b1b09cb1a790110640f1c9e5d56fe9f7 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Fri, 29 Oct 2021 10:32:41 -0400 Subject: Fix Linker generator ID (#4601) The generator ID is located in the upper 16 bits. The lower bits are reserved for a version number. Co-authored-by: Lionel Landwerlin --- source/link/linker.cpp | 3 ++- source/spirv_constant.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/link/linker.cpp b/source/link/linker.cpp index 8da4a98d..c5ca5625 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -34,6 +34,7 @@ #include "source/opt/pass_manager.h" #include "source/opt/remove_duplicates_pass.h" #include "source/opt/type_manager.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/make_unique.h" #include "spirv-tools/libspirv.hpp" @@ -207,7 +208,7 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer, header->magic_number = SpvMagicNumber; header->version = version; - header->generator = 17u; + header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0); header->bound = max_id_bound; header->reserved = 0u; diff --git a/source/spirv_constant.h b/source/spirv_constant.h index 39771ccb..8636806c 100644 --- a/source/spirv_constant.h +++ b/source/spirv_constant.h @@ -84,6 +84,7 @@ typedef enum spv_generator_t { SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6, SPV_GENERATOR_KHRONOS_ASSEMBLER = 7, SPV_GENERATOR_KHRONOS_GLSLANG = 8, + SPV_GENERATOR_KHRONOS_LINKER = 17, SPV_GENERATOR_NUM_ENTRIES, SPV_FORCE_16_BIT_ENUM(spv_generator_t) } spv_generator_t; -- cgit v1.2.3 From 6c7885dbdeaeea37e1f102efacf839beddbf59d7 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Fri, 29 Oct 2021 10:46:43 -0400 Subject: Change branch handling in ADCE to fix errors (#4596) Consider the new test case. The conditional branch in the continue block is never marked as live. However, `IsDead` will say it is not dead, so it does not get deleted. Because it was never marked as live, `%false` was not mark as live either, but it gets deleted. This results in invalid code. To fix this properly, we had to reconsider how branches are handle. We make the following changes: 1) Terminator instructions that are not branch or OpUnreachable must be kept, so they are marked as live when initializing the worklist. 2) Branches and OpUnreachable instructions are marked as live if a) the block does not have a merge instruction and another instruction in the block is marked as live, or b) the merge instruction in the same block is marked as live. 3) Any instruction that is not marked as live is removed. 4) If a terminator is to be removed, an OpUnreachable is added. This happens when the entire block is dead, and the block will be removed. The OpUnreachable is generated to make sure the block still has a terminator, and is valid. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4509. --- source/opt/aggressive_dead_code_elim_pass.cpp | 125 +++++++++++++++----------- source/opt/aggressive_dead_code_elim_pass.h | 13 ++- test/opt/aggressive_dead_code_elim_test.cpp | 34 +++++-- 3 files changed, 113 insertions(+), 59 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index c83fb48c..0b54d5e8 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG Inc. -// Copyright (c) 2018 Google LLC +// Copyright (c) 2018-2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ #include "source/cfa.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/eliminate_dead_functions_util.h" +#include "source/opt/ir_builder.h" #include "source/opt/iterator.h" #include "source/opt/reflect.h" #include "source/spirv_constant.h" @@ -166,14 +167,6 @@ bool AggressiveDCEPass::AllExtensionsSupported() const { return true; } -bool AggressiveDCEPass::IsDead(Instruction* inst) { - if (IsLive(inst)) return false; - if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) && - context()->get_instr_block(inst)->GetMergeInst() == nullptr) - return false; - return true; -} - bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { const uint32_t tId = inst->GetSingleWordInOperand(0); Instruction* tInst = get_def_use_mgr()->GetDef(tId); @@ -190,7 +183,7 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { }); return dead; } - return IsDead(tInst); + return !IsLive(tInst); } void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { @@ -285,7 +278,7 @@ bool AggressiveDCEPass::KillDeadInstructions( for (auto bi = structured_order.begin(); bi != structured_order.end();) { uint32_t merge_block_id = 0; (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) { - if (!IsDead(inst)) return; + if (IsLive(inst)) return; if (inst->opcode() == SpvOpLabel) return; // If dead instruction is selection merge, remember merge block // for new branch at end of block @@ -323,6 +316,12 @@ bool AggressiveDCEPass::KillDeadInstructions( live_insts_.Set(merge_terminator->unique_id()); } } else { + Instruction* inst = (*bi)->terminator(); + if (!IsLive(inst)) { + // If the terminator is not live, this block has no live instructions, + // and it will be unreachable. + AddUnreachable(*bi); + } ++bi; } } @@ -457,26 +456,38 @@ uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) { void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) { BasicBlock* basic_block = context()->get_instr_block(inst); - if (basic_block != nullptr) { - AddToWorklist(basic_block->GetLabelInst()); + if (basic_block == nullptr) { + return; } - // If in a structured if or loop construct, add the controlling - // conditional branch and its merge. - BasicBlock* blk = context()->get_instr_block(inst); - Instruction* branchInst = GetHeaderBranch(blk); - if (branchInst != nullptr) { - AddToWorklist(branchInst); - Instruction* mergeInst = GetMergeInstruction(branchInst); - AddToWorklist(mergeInst); + // If we intend to keep this instruction, we need the block label and + // block terminator to have a valid block for the instruction. + AddToWorklist(basic_block->GetLabelInst()); + + // We need to mark the successors blocks that follow as live. If this is + // header of the merge construct, the construct may be folded, but we will + // definitely need the merge label. If it is not a construct, the terminator + // must be live, and the successor blocks will be marked as live when + // processing the terminator. + uint32_t merge_id = basic_block->MergeBlockIdIfAny(); + if (merge_id == 0) { + AddToWorklist(basic_block->terminator()); + } else { + AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id)); } - // If the block is a header, add the next outermost controlling - // conditional branch and its merge. - Instruction* nextBranchInst = GetBranchForNextHeader(blk); - if (nextBranchInst != nullptr) { - AddToWorklist(nextBranchInst); - Instruction* mergeInst = GetMergeInstruction(nextBranchInst); + // Mark the structured control flow constructs that contains this block as + // live. If |inst| is an instruction in the loop header, then it is part of + // the loop, so the loop construct must be live. We exclude the label because + // it does not matter how many times it is executed. This could be extended + // to more instructions, but we will need it for now. + if (inst->opcode() != SpvOpLabel) + MarkLoopConstructAsLiveIfLoopHeader(basic_block); + + Instruction* next_branch_inst = GetBranchForNextHeader(basic_block); + if (next_branch_inst != nullptr) { + AddToWorklist(next_branch_inst); + Instruction* mergeInst = GetMergeInstruction(next_branch_inst); AddToWorklist(mergeInst); } @@ -485,14 +496,20 @@ void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) { AddBreaksAndContinuesToWorklist(inst); } } +void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader( + BasicBlock* basic_block) { + // If this is the header for a loop, then loop structure needs to keep as well + // because the loop header is also part of the loop. + Instruction* merge_inst = basic_block->GetLoopMergeInst(); + if (merge_inst != nullptr) { + AddToWorklist(basic_block->terminator()); + AddToWorklist(merge_inst); + } +} void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) { - inst->ForEachInId([&inst, this](const uint32_t* iid) { + inst->ForEachInId([this](const uint32_t* iid) { Instruction* inInst = get_def_use_mgr()->GetDef(*iid); - // Do not add label if an operand of a branch. This is not needed - // as part of live code discovery and can create false live code, - // for example, the branch to a header of a loop. - if (inInst->opcode() == SpvOpLabel && inst->IsBranch()) return; AddToWorklist(inInst); }); if (inst->type_id() != 0) { @@ -504,6 +521,7 @@ void AggressiveDCEPass::InitializeWorkList( Function* func, std::list& structured_order) { AddToWorklist(&func->DefInst()); MarkFunctionParameterAsLive(func); + MarkFirstBlockAsLive(func); // Add instructions with external side effects to the worklist. Also add // branches that are not attached to a structured construct. @@ -512,6 +530,9 @@ void AggressiveDCEPass::InitializeWorkList( for (auto& bi : structured_order) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { SpvOp op = ii->opcode(); + if (ii->IsBranch()) { + continue; + } switch (op) { case SpvOpStore: { uint32_t var_id = 0; @@ -526,19 +547,9 @@ void AggressiveDCEPass::InitializeWorkList( (void)GetPtr(target_addr_id, &var_id); if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); } break; - case SpvOpSwitch: - case SpvOpBranch: - case SpvOpBranchConditional: - case SpvOpUnreachable: { - bool branch_related_to_construct = - (GetMergeInstruction(&*ii) == nullptr && - GetHeaderBlock(context()->get_instr_block(&*ii)) == nullptr); - if (branch_related_to_construct) { - AddToWorklist(&*ii); - } - } break; case SpvOpLoopMerge: case SpvOpSelectionMerge: + case SpvOpUnreachable: break; default: { // Function calls, atomics, function params, function returns, etc. @@ -762,7 +773,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2); Instruction* counter_buffer_inst = get_def_use_mgr()->GetDef(counter_buffer_id); - if (IsDead(counter_buffer_inst)) { + if (!IsLive(counter_buffer_inst)) { context()->KillInst(annotation); modified = true; } @@ -777,7 +788,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { for (uint32_t i = 1; i < annotation->NumOperands();) { Instruction* opInst = get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); - if (IsDead(opInst)) { + if (!IsLive(opInst)) { // Don't increment |i|. annotation->RemoveOperand(i); modified = true; @@ -804,7 +815,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { for (uint32_t i = 1; i < annotation->NumOperands();) { Instruction* opInst = get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); - if (IsDead(opInst)) { + if (!IsLive(opInst)) { // Don't increment |i|. annotation->RemoveOperand(i + 1); annotation->RemoveOperand(i); @@ -838,13 +849,13 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } for (auto& dbg : get_module()->ext_inst_debuginfo()) { - if (!IsDead(&dbg)) continue; + if (IsLive(&dbg)) continue; // Save GlobalVariable if its variable is live, otherwise null out variable // index if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex); Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); - if (!IsDead(var_inst)) continue; + if (IsLive(var_inst)) continue; context()->ForgetUses(&dbg); dbg.SetOperand( kGlobalVariableVariableIndex, @@ -859,7 +870,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { // Since ADCE is disabled for non-shaders, we don't check for export linkage // attributes here. for (auto& val : get_module()->types_values()) { - if (IsDead(&val)) { + if (!IsLive(&val)) { // Save forwarded pointer if pointer is live since closure does not mark // this live as it does not have a result id. This is a little too // conservative since it is not known if the structure type that needed @@ -867,7 +878,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { if (val.opcode() == SpvOpTypeForwardPointer) { uint32_t ptr_ty_id = val.GetSingleWordInOperand(0); Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id); - if (!IsDead(ptr_ty_inst)) continue; + if (IsLive(ptr_ty_inst)) continue; } to_kill_.push_back(&val); modified = true; @@ -886,7 +897,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } else { auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); - if (!IsDead(var)) { + if (IsLive(var)) { new_operands.push_back(entry.GetInOperand(i)); } } @@ -1063,5 +1074,17 @@ bool AggressiveDCEPass::HasCall(Function* func) { [](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; }); } +void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) { + BasicBlock* first_block = &*func->begin(); + MarkBlockAsLive(first_block->GetLabelInst()); +} + +void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) { + InstructionBuilder builder( + context(), block, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + builder.AddUnreachable(); +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index 7d1679f6..1b3fd1e8 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h @@ -77,9 +77,6 @@ class AggressiveDCEPass : public MemPass { return live_insts_.Get(inst->unique_id()); } - // Returns true if |inst| is dead. - bool IsDead(Instruction* inst); - // Adds entry points, execution modes and workgroup size decorations to the // worklist for processing with the first function. void InitializeModuleScopeLiveInstructions(); @@ -217,6 +214,16 @@ class AggressiveDCEPass : public MemPass { // Returns true if |func| contains a function call. bool HasCall(Function* func); + // Marks the first block, which is the entry block, in |func| as live. + void MarkFirstBlockAsLive(Function* func); + + // Adds an OpUnreachable instruction at the end of |block|. + void AddUnreachable(BasicBlock*& block); + + // Marks the OpLoopMerge and the terminator in |basic_block| as live if + // |basic_block| is a loop header. + void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block); + // The cached results for |IsEntryPointWithNoCalls|. It maps the function's // result id to the return value. std::unordered_map entry_point_with_no_calls_cache_; diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 746569ab..f228c8cb 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -8008,11 +8008,35 @@ OpFunctionEnd EXPECT_EQ(text, std::get<0>(result)); } -// TODO(greg-lunarg): Add tests to verify handling of these cases: -// -// Check that logical addressing required -// Check that function calls inhibit optimization -// Others? +TEST_F(AggressiveDCETest, EmptyContinueWithConditionalBranch) { + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%2 = OpFunction %void None %4 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpKill +%12 = OpLabel +OpBranchConditional %false %10 %10 +%11 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt -- cgit v1.2.3 From c194bb2a7ff2d0e3e6076965f7bdea37dc3dc54b Mon Sep 17 00:00:00 2001 From: sfricke-samsung <46493288+sfricke-samsung@users.noreply.github.com> Date: Fri, 29 Oct 2021 08:58:02 -0700 Subject: spirv-val Update LocalSizeId VUID (#4602) --- source/val/validate_mode_setting.cpp | 8 +++---- source/val/validation_state.cpp | 4 ++-- test/val/val_modes_test.cpp | 43 ++++++++++++++++++++++++++++++++---- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index 4fb6d9b9..96352687 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp @@ -235,11 +235,11 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { } if (!ok) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4683) + << _.VkErrorID(6426) << "In the Vulkan environment, GLCompute execution model " - "entry points require either the LocalSize execution " - "mode or an object decorated with WorkgroupSize must be " - "specified."; + "entry points require either the LocalSize or " + "LocalSizeId execution mode or an object decorated with " + "WorkgroupSize must be specified."; } } break; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 10ebd68c..79638727 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -1845,8 +1845,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); case 4682: return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); - case 4683: - return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683); + case 6426: + return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683 case 4685: return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); case 4686: diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index d060bb79..02a61327 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -63,12 +63,13 @@ OpEntryPoint GLCompute %main "main" CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-LocalSize-04683")); + AnyVUID("VUID-StandaloneSpirv-LocalSize-06426")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("In the Vulkan environment, GLCompute execution model entry " - "points require either the LocalSize execution mode or an " - "object decorated with WorkgroupSize must be specified.")); + HasSubstr( + "In the Vulkan environment, GLCompute execution model entry " + "points require either the LocalSize or LocalSizeId execution mode " + "or an object decorated with WorkgroupSize must be specified.")); } TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) { @@ -101,6 +102,40 @@ OpExecutionMode %main LocalSize 1 1 1 EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("LocalSizeId mode is not allowed by the current environment.")); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdGood) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 + CompileSuccessfully(spirv, env); + spvValidatorOptionsSetAllowLocalSizeId(getValidatorOptions(), true); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) { const std::string spirv = R"( OpCapability Shader -- cgit v1.2.3 From e3a373f2b79997326cb9330d4f3192b9b5682966 Mon Sep 17 00:00:00 2001 From: timlyeee <43355299+timlyeee@users.noreply.github.com> Date: Sat, 30 Oct 2021 02:04:59 +0800 Subject: Make cxx exceptions controllable (#4591) * Make cxx exceptions controllable Found a possible link error if we compile spirv-tools by using VS2019 and link with VS2017 for another project, unresolved symbols as _CxxFrameHandler4 and __GSHandlerCheck_EH4 will be thrown. As Visual Studio updated its c++ exceptions libs. https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/ So we are making cxx exceptions controllable via a CMake option `ENABLE_EXCEPTIONS_ON_MSVC`. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e3b16f..70caf857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,7 @@ endif() # Note this target provides no API stability guarantees. # # Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909. +option(ENABLE_EXCEPTIONS_ON_MSVC "Build SPIRV-TOOLS with c++ exceptions enabled in MSVC" ON) option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON) if(SPIRV_TOOLS_BUILD_STATIC) set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static) @@ -215,7 +216,9 @@ function(spvtools_default_compile_options TARGET) if (MSVC) # Specify /EHs for exception handling. This makes using SPIRV-Tools as # dependencies in other projects easier. - target_compile_options(${TARGET} PRIVATE /EHs) + if(ENABLE_EXCEPTIONS_ON_MSVC) + target_compile_options(${TARGET} PRIVATE /EHs) + endif() endif() # For MinGW cross compile, statically link to the C++ runtime. -- cgit v1.2.3 From e3c7098104d3bd46fb152010a10cefb26d9f65b4 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Fri, 29 Oct 2021 20:05:45 +0200 Subject: In generated cmake config file for SPIRV-Tools only access cmake target, if present (#4590) Fixes #4582 --- source/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 898fcfd3..331ff675 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -428,8 +428,10 @@ if(ENABLE_SPIRV_TOOLS_INSTALL) # Special config file for root library compared to other libs. file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake "include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n" - "set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" - "get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n") + "if(TARGET ${SPIRV_TOOLS})\n" + " set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" + " get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n" + "endif()\n") install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR}) endif(ENABLE_SPIRV_TOOLS_INSTALL) -- cgit v1.2.3 From 97d449560085c841c1368200b6f41aace1c848ba Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Fri, 29 Oct 2021 20:36:51 +0100 Subject: Avoid bitwise and in boolean expression (#4603) In #3404 a logical && was replaced with a bitwise & to ensure that both side-effecting arguments were evaluated. However, this leads to warnings from some compilers (leading to the OSS-Fuzz build breaking, in particular). This change reworks the relevant code so that both arguments to the logical && are evaluated into temporaries. --- source/val/validate_cfg.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index c8e26fc1..7842e56d 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -659,9 +659,9 @@ spv_result_t ValidateStructuredSelections( // Mark the upcoming blocks as seen now, but only error out if this block // was missing a merge instruction and both labels hadn't been seen // previously. - const bool both_unseen = - seen.insert(true_label).second & seen.insert(false_label).second; - if (!merge && both_unseen) { + const bool true_label_unseen = seen.insert(true_label).second; + const bool false_label_unseen = seen.insert(false_label).second; + if (!merge && true_label_unseen && false_label_unseen) { return _.diag(SPV_ERROR_INVALID_CFG, terminator) << "Selection must be structured"; } -- cgit v1.2.3 From 1082de6bb32fc425623a6a218c5295bfd4db9a09 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 1 Nov 2021 08:45:32 -0400 Subject: Handle overflowing id in merge return (#4606) If the ids overflow when creating an integer constant in the ir_builder, there will be a nullptr dereference. This is happening from inside merge return. We need to propagate the error up, and make sure it is handled appropriately. --- source/opt/ir_builder.h | 22 +++++++++++++++------- source/opt/merge_return_pass.cpp | 21 +++++++++++++++------ source/opt/merge_return_pass.h | 4 ++-- test/opt/pass_merge_return_test.cpp | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h index fe5feff5..4433cf0d 100644 --- a/source/opt/ir_builder.h +++ b/source/opt/ir_builder.h @@ -359,8 +359,9 @@ class InstructionBuilder { return AddInstruction(std::move(select)); } - // Adds a signed int32 constant to the binary. - // The |value| parameter is the constant value to be added. + // Returns a pointer to the definition of a signed 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. Instruction* GetSintConstant(int32_t value) { return GetIntConstant(value, true); } @@ -381,21 +382,24 @@ class InstructionBuilder { GetContext()->TakeNextId(), ops)); return AddInstruction(std::move(construct)); } - // Adds an unsigned int32 constant to the binary. - // The |value| parameter is the constant value to be added. + + // Returns a pointer to the definition of an unsigned 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. Instruction* GetUintConstant(uint32_t value) { return GetIntConstant(value, false); } uint32_t GetUintConstantId(uint32_t value) { Instruction* uint_inst = GetUintConstant(value); - return uint_inst->result_id(); + return (uint_inst != nullptr ? uint_inst->result_id() : 0); } // Adds either a signed or unsigned 32 bit integer constant to the binary - // depedning on the |sign|. If |sign| is true then the value is added as a + // depending on the |sign|. If |sign| is true then the value is added as a // signed constant otherwise as an unsigned constant. If |sign| is false the - // value must not be a negative number. + // value must not be a negative number. Returns false if the constant does + // not exists and could be be created. template Instruction* GetIntConstant(T value, bool sign) { // Assert that we are not trying to store a negative number in an unsigned @@ -411,6 +415,10 @@ class InstructionBuilder { uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); + if (type_id == 0) { + return nullptr; + } + // Get the memory managed type so that it is safe to be stored by // GetConstant. analysis::Type* rebuilt_type = diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp index f1601049..a962a7cc 100644 --- a/source/opt/merge_return_pass.cpp +++ b/source/opt/merge_return_pass.cpp @@ -111,7 +111,9 @@ bool MergeReturnPass::ProcessStructured( } RecordImmediateDominators(function); - AddSingleCaseSwitchAroundFunction(); + if (!AddSingleCaseSwitchAroundFunction()) { + return false; + } std::list order; cfg()->ComputeStructuredOrder(function, &*function->begin(), &order); @@ -770,7 +772,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element, list->insert(pos, new_element); } -void MergeReturnPass::AddSingleCaseSwitchAroundFunction() { +bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() { CreateReturnBlock(); CreateReturn(final_return_block_); @@ -778,7 +780,10 @@ void MergeReturnPass::AddSingleCaseSwitchAroundFunction() { cfg()->RegisterBlock(final_return_block_); } - CreateSingleCaseSwitch(final_return_block_); + if (!CreateSingleCaseSwitch(final_return_block_)) { + return false; + } + return true; } BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { @@ -813,7 +818,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { return new_block; } -void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { +bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { // Insert the switch before any code is run. We have to split the entry // block to make sure the OpVariable instructions remain in the entry block. BasicBlock* start_block = &*function_->begin(); @@ -830,13 +835,17 @@ void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { context(), start_block, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {}, - merge_target->id()); + uint32_t const_zero_id = builder.GetUintConstantId(0u); + if (const_zero_id == 0) { + return false; + } + builder.AddSwitch(const_zero_id, old_block->id(), {}, merge_target->id()); if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { cfg()->RegisterBlock(old_block); cfg()->AddEdges(start_block); } + return true; } bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) { diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h index 06a3e7b5..4096ce7d 100644 --- a/source/opt/merge_return_pass.h +++ b/source/opt/merge_return_pass.h @@ -277,7 +277,7 @@ class MergeReturnPass : public MemPass { // current function where the switch and case value are both zero and the // default is the merge block. Returns after the switch is executed. Sets // |final_return_block_|. - void AddSingleCaseSwitchAroundFunction(); + bool AddSingleCaseSwitchAroundFunction(); // Creates a new basic block that branches to |header_label_id|. Returns the // new basic block. The block will be the second last basic block in the @@ -286,7 +286,7 @@ class MergeReturnPass : public MemPass { // Creates a one case switch around the executable code of the function with // |merge_target| as the merge node. - void CreateSingleCaseSwitch(BasicBlock* merge_target); + bool CreateSingleCaseSwitch(BasicBlock* merge_target); // Returns true if |function| has an unreachable block that is not a continue // target that simply branches back to the header, or a merge block containing diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp index fd97efab..21960d17 100644 --- a/test/opt/pass_merge_return_test.cpp +++ b/test/opt/pass_merge_return_test.cpp @@ -2567,6 +2567,39 @@ TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) { SinglePassRunAndMatch(before, true); } +TEST_F(MergeReturnPassTest, OverflowTest1) { + const std::string text = + R"( +; CHECK: OpReturn +; CHECK-NOT: OpReturn +; CHECK: OpFunctionEnd + OpCapability ClipDistance + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %6 = OpTypeFunction %void + %2 = OpFunction %void None %6 + %4194303 = OpLabel + OpBranch %18 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranch %21 + %21 = OpLabel + OpReturn + %20 = OpLabel + OpBranch %18 + %19 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = + SinglePassRunToBinary(text, /* skip_nop = */ true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From 0d0013002abc5f9b49c202500e5c96a498eb7575 Mon Sep 17 00:00:00 2001 From: Pelle Johnsen Date: Mon, 1 Nov 2021 21:45:51 +0100 Subject: Add Wasm build (#3752) * Add wasm build * Run wasm ci on push * Add copyright notice to wasm files * [wasm] Update Emscripten * [wasm] Change global lambda to regular function * [wasm] Show detected core count during build * [wasm] Set JS version from CHANGES, GITHUB_RUN_ID Also remove custom docker emscripten build with brotli, as not used * [wasm] Change github actions to use npm-publish * [wasm] Us docker-compose up for CI * [wasm] pass GITHUB_RUN_ID to docker * [wasm] Change GITHUB_RUN_ID to GITHUB_RUN_NUMBER * [wasm] Fix GITHUB_RUN_NUMBER in docker-compose.yml --- .github/workflows/wasm.yml | 20 ++++++++++ docker-compose.yml | 10 +++++ source/wasm/README.md | 43 ++++++++++++++++++++ source/wasm/build.sh | 78 +++++++++++++++++++++++++++++++++++++ source/wasm/package.json | 17 ++++++++ source/wasm/spirv-tools.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++ source/wasm/spirv-tools.d.ts | 56 ++++++++++++++++++++++++++ test/wasm/test.js | 64 ++++++++++++++++++++++++++++++ 8 files changed, 381 insertions(+) create mode 100644 .github/workflows/wasm.yml create mode 100644 docker-compose.yml create mode 100644 source/wasm/README.md create mode 100755 source/wasm/build.sh create mode 100644 source/wasm/package.json create mode 100644 source/wasm/spirv-tools.cpp create mode 100644 source/wasm/spirv-tools.d.ts create mode 100644 test/wasm/test.js diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 00000000..eef0e2c1 --- /dev/null +++ b/.github/workflows/wasm.yml @@ -0,0 +1,20 @@ +name: Wasm Build + +on: push + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build web + run: docker-compose up + - name: Run tests + run: node test/wasm/test.js + - name: Publish to npm + uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ secrets.NPM_TOKEN }} + package: ./out/web/package.json + dry-run: true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..fb6d114f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3" +services: + build: + image: emscripten/emsdk:2.0.2 + environment: + GITHUB_RUN_NUMBER: ${GITHUB_RUN_NUMBER:-} + working_dir: /app + command: ./source/wasm/build.sh + volumes: + - ./:/app diff --git a/source/wasm/README.md b/source/wasm/README.md new file mode 100644 index 00000000..aca0f70c --- /dev/null +++ b/source/wasm/README.md @@ -0,0 +1,43 @@ +# SPIRV-Tools + +Wasm (WebAssembly) build of https://github.com/KhronosGroup/SPIRV-Tools + +## Usage + +```js +const spirvTools = require("spirv-tools"); + +const test = async () => { + // Load the library + const spv = await spirvTools(); + + // assemble + const source = ` + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpSource GLSL 450 + OpDecorate %spec SpecId 1 + %int = OpTypeInt 32 1 + %spec = OpSpecConstant %int 0 + %const = OpConstant %int 42`; + const asResult = spv.as( + source, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_TEXT_TO_BINARY_OPTION_NONE + ); + console.log(`as returned ${asResult.byteLength} bytes`); + + // re-disassemble + const disResult = spv.dis( + asResult, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis:\n", disResult); +}; + +test(); +``` diff --git a/source/wasm/build.sh b/source/wasm/build.sh new file mode 100755 index 00000000..f02ae525 --- /dev/null +++ b/source/wasm/build.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# Copyright (c) 2020 The Khronos Group 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. + +set -e + +NUM_CORES=$(nproc) +echo "Detected $NUM_CORES cores for building" + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +VERSION=$(sed -n '0,/^v20/ s/^v\(20[0-9.]*\).*/\1/p' $DIR/../../CHANGES).${GITHUB_RUN_NUMBER:-0} +echo "Version: $VERSION" + +build() { + type=$1 + shift + args=$@ + mkdir -p build/$type + pushd build/$type + echo $args + emcmake cmake \ + -DCMAKE_BUILD_TYPE=Release \ + $args \ + ../.. + emmake make -j $(( $NUM_CORES )) SPIRV-Tools-static + + echo Building js interface + emcc \ + --bind \ + -I../../include \ + -std=c++11 \ + ../../source/wasm/spirv-tools.cpp \ + source/libSPIRV-Tools.a \ + -o spirv-tools.js \ + -s MODULARIZE \ + -Oz + + popd + mkdir -p out/$type + + # copy other js files + cp source/wasm/spirv-tools.d.ts out/$type/ + sed -e 's/\("version"\s*:\s*\).*/\1"'$VERSION'",/' source/wasm/package.json > out/$type/package.json + cp source/wasm/README.md out/$type/ + cp LICENSE out/$type/ + + cp build/$type/spirv-tools.js out/$type/ + gzip -9 -k -f out/$type/spirv-tools.js + if [ -e build/$type/spirv-tools.wasm ] ; then + cp build/$type/spirv-tools.wasm out/$type/ + gzip -9 -k -f out/$type/spirv-tools.wasm + fi +} + +if [ ! -d external/spirv-headers ] ; then + echo "Fetching SPIRV-headers" + git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers +fi + +echo Building ${BASH_REMATCH[1]} +build web\ + -DSPIRV_COLOR_TERMINAL=OFF\ + -DSPIRV_SKIP_TESTS=ON\ + -DSPIRV_SKIP_EXECUTABLES=ON + +wc -c out/*/* diff --git a/source/wasm/package.json b/source/wasm/package.json new file mode 100644 index 00000000..78273538 --- /dev/null +++ b/source/wasm/package.json @@ -0,0 +1,17 @@ +{ + "name": "spirv-tools", + "version": "VERSION", + "license": "Apache-2.0", + "main": "spirv-tools", + "types": "spirv-tools.d.ts", + "files": [ + "*.wasm", + "*.js", + "*.d.ts" + ], + "repository": { + "type": "git", + "url": "https://github.com/KhronosGroup/SPIRV-Tools" + }, + "homepage": "https://github.com/KhronosGroup/SPIRV-Tools" +} diff --git a/source/wasm/spirv-tools.cpp b/source/wasm/spirv-tools.cpp new file mode 100644 index 00000000..90407f32 --- /dev/null +++ b/source/wasm/spirv-tools.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2020 The Khronos Group 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 "spirv-tools/libspirv.hpp" + +#include +#include +#include + +#include +#include +using namespace emscripten; + +void print_msg_to_stderr (spv_message_level_t, const char*, + const spv_position_t&, const char* m) { + std::cerr << "error: " << m << std::endl; +}; + +std::string dis(std::string const& buffer, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + const uint32_t* ptr = reinterpret_cast(buffer.data()); + spirv.assign(ptr, ptr + buffer.size() / 4); + std::string disassembly; + if (!core.Disassemble(spirv, &disassembly, options)) return "Error"; + return disassembly; +} + +emscripten::val as(std::string const& source, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + if (!core.Assemble(source, &spirv, options)) spirv.clear(); + const uint8_t* ptr = reinterpret_cast(spirv.data()); + return emscripten::val(emscripten::typed_memory_view(spirv.size() * 4, + ptr)); +} + +EMSCRIPTEN_BINDINGS(my_module) { + function("dis", &dis); + function("as", &as); + + constant("SPV_ENV_UNIVERSAL_1_0", static_cast(SPV_ENV_UNIVERSAL_1_0)); + constant("SPV_ENV_VULKAN_1_0", static_cast(SPV_ENV_VULKAN_1_0)); + constant("SPV_ENV_UNIVERSAL_1_1", static_cast(SPV_ENV_UNIVERSAL_1_1)); + constant("SPV_ENV_OPENCL_2_1", static_cast(SPV_ENV_OPENCL_2_1)); + constant("SPV_ENV_OPENCL_2_2", static_cast(SPV_ENV_OPENCL_2_2)); + constant("SPV_ENV_OPENGL_4_0", static_cast(SPV_ENV_OPENGL_4_0)); + constant("SPV_ENV_OPENGL_4_1", static_cast(SPV_ENV_OPENGL_4_1)); + constant("SPV_ENV_OPENGL_4_2", static_cast(SPV_ENV_OPENGL_4_2)); + constant("SPV_ENV_OPENGL_4_3", static_cast(SPV_ENV_OPENGL_4_3)); + constant("SPV_ENV_OPENGL_4_5", static_cast(SPV_ENV_OPENGL_4_5)); + constant("SPV_ENV_UNIVERSAL_1_2", static_cast(SPV_ENV_UNIVERSAL_1_2)); + constant("SPV_ENV_OPENCL_1_2", static_cast(SPV_ENV_OPENCL_1_2)); + constant("SPV_ENV_OPENCL_EMBEDDED_1_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_1_2)); + constant("SPV_ENV_OPENCL_2_0", static_cast(SPV_ENV_OPENCL_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_0", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_1", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_1)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_2)); + constant("SPV_ENV_UNIVERSAL_1_3", static_cast(SPV_ENV_UNIVERSAL_1_3)); + constant("SPV_ENV_VULKAN_1_1", static_cast(SPV_ENV_VULKAN_1_1)); + constant("SPV_ENV_WEBGPU_0", static_cast(SPV_ENV_WEBGPU_0)); + constant("SPV_ENV_UNIVERSAL_1_4", static_cast(SPV_ENV_UNIVERSAL_1_4)); + constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + constant("SPV_ENV_UNIVERSAL_1_5", static_cast(SPV_ENV_UNIVERSAL_1_5)); + constant("SPV_ENV_VULKAN_1_2", static_cast(SPV_ENV_VULKAN_1_2)); + + + constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast(SPV_BINARY_TO_TEXT_OPTION_NONE)); + constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast(SPV_BINARY_TO_TEXT_OPTION_PRINT)); + constant("SPV_BINARY_TO_TEXT_OPTION_COLOR", static_cast(SPV_BINARY_TO_TEXT_OPTION_COLOR)); + constant("SPV_BINARY_TO_TEXT_OPTION_INDENT", static_cast(SPV_BINARY_TO_TEXT_OPTION_INDENT)); + constant("SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET", static_cast(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET)); + constant("SPV_BINARY_TO_TEXT_OPTION_NO_HEADER", static_cast(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)); + constant("SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES", static_cast(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)); + + constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast(SPV_TEXT_TO_BINARY_OPTION_NONE)); + constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)); +} \ No newline at end of file diff --git a/source/wasm/spirv-tools.d.ts b/source/wasm/spirv-tools.d.ts new file mode 100644 index 00000000..9c197973 --- /dev/null +++ b/source/wasm/spirv-tools.d.ts @@ -0,0 +1,56 @@ +// Copyright (c) 2020 The Khronos Group 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. + +declare interface SpirvTools { + as(input: string, env: number, options: number): Uint8Array; + dis(input: Uint8Array, env: number, options: number): string; + + SPV_ENV_UNIVERSAL_1_0: number; + SPV_ENV_VULKAN_1_0: number; + SPV_ENV_UNIVERSAL_1_1: number; + SPV_ENV_OPENCL_2_1: number; + SPV_ENV_OPENCL_2_2: number; + SPV_ENV_OPENGL_4_0: number; + SPV_ENV_OPENGL_4_1: number; + SPV_ENV_OPENGL_4_2: number; + SPV_ENV_OPENGL_4_3: number; + SPV_ENV_OPENGL_4_5: number; + SPV_ENV_UNIVERSAL_1_2: number; + SPV_ENV_OPENCL_1_2: number; + SPV_ENV_OPENCL_EMBEDDED_1_2: number; + SPV_ENV_OPENCL_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_1: number; + SPV_ENV_OPENCL_EMBEDDED_2_2: number; + SPV_ENV_UNIVERSAL_1_3: number; + SPV_ENV_VULKAN_1_1: number; + SPV_ENV_WEBGPU_0: number; + SPV_ENV_UNIVERSAL_1_4: number; + SPV_ENV_VULKAN_1_1_SPIRV_1_4: number; + SPV_ENV_UNIVERSAL_1_5: number; + SPV_ENV_VULKAN_1_2: number; + + SPV_TEXT_TO_BINARY_OPTION_NONE: number; + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS: number; + + SPV_BINARY_TO_TEXT_OPTION_NONE: number; + SPV_BINARY_TO_TEXT_OPTION_PRINT: number; + SPV_BINARY_TO_TEXT_OPTION_COLOR: number; + SPV_BINARY_TO_TEXT_OPTION_INDENT: number; + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET: number; + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER: number; + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES: number; +} + +export default function (): Promise; diff --git a/test/wasm/test.js b/test/wasm/test.js new file mode 100644 index 00000000..7f0d8f3c --- /dev/null +++ b/test/wasm/test.js @@ -0,0 +1,64 @@ +// Copyright (c) 2020 The Khronos Group 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. + +const spirvTools = require("../../out/web/spirv-tools"); +const fs = require("fs"); +const util = require("util"); +const readFile = util.promisify(fs.readFile); + +const SPV_PATH = "./test/fuzzers/corpora/spv/simple.spv"; + +const test = async () => { + const spv = await spirvTools(); + + // disassemble from file + const buffer = await readFile(SPV_PATH); + const disFileResult = spv.dis( + buffer, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis from file:\n", disFileResult); + + // assemble + const source = ` + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpSource GLSL 450 + OpDecorate %spec SpecId 1 + %int = OpTypeInt 32 1 + %spec = OpSpecConstant %int 0 + %const = OpConstant %int 42`; + const asResult = spv.as( + source, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_TEXT_TO_BINARY_OPTION_NONE + ); + console.log(`as returned ${asResult.byteLength} bytes`); + + // re-disassemble + const disResult = spv.dis( + asResult, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis:\n", disResult); +}; + +test(); -- cgit v1.2.3 From d645ea270504fa9e577905c03e1da086e4fb0c73 Mon Sep 17 00:00:00 2001 From: David Neto Date: Tue, 2 Nov 2021 08:56:06 -0400 Subject: Update CHANGES, README for WebAssembly build (#4609) --- CHANGES | 2 +- README.md | 28 +++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 0364c25d..c905ad49 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,7 @@ Revision history for SPIRV-Tools v2021.4-dev 2021-08-25 - - Start v2021.4-dev + - Add a WebAssembly build v2021.3 2021-08-24 - General diff --git a/README.md b/README.md index f53db165..5cb1b343 100644 --- a/README.md +++ b/README.md @@ -319,7 +319,7 @@ tests: *Note*: Prebuilt binaries are available from the [downloads](docs/downloads.md) page. First [get the sources](#getting-the-source). -Then build using CMake, Bazel, or Android ndk-build. +Then build using CMake, Bazel, Android ndk-build, or the Emscripten SDK. ### Build using CMake You can build the project using [CMake][cmake]: @@ -364,6 +364,30 @@ You can also use [Bazel](https://bazel.build/) to build the project. cd bazel build :all ``` +### Build a node.js package using Emscripten + +The SPIRV-Tools core library can be built to a WebAssembly [node.js](https://nodejs.org) +module. The resulting `SpirvTools` WebAssembly module only exports methods to +assemble and disassemble SPIR-V modules. + +First, make sure you have the [Emscripten SDK](https://emscripten.org). +Then: + +```sh +cd +./source/wasm/build.sh +``` + +The resulting node package, with JavaScript and TypeScript bindings, is +written to `/out/web`. + +Note: This builds the package locally. It does *not* publish it to [npm](https://npmjs.org). + +To test the result: + +```sh +node ./test/wasm/test.js +``` ### Tools you'll need @@ -377,6 +401,8 @@ suite. - [Bazel](https://bazel.build/) (optional): if building the source with Bazel, you need to install Bazel Version 0.29.1 on your machine. Other versions may also work, but are not verified. +- [Emscripten SDK](https://emscripten.org) (optional): if building the + WebAssembly module. SPIRV-Tools is regularly tested with the following compilers: -- cgit v1.2.3 From 66ef7cb68e8e19afee9e023ca9dc029e2c90559d Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 3 Nov 2021 11:04:04 -0400 Subject: Remove publish-to-npm step for Wasm build (#4610) The JS-Tools/npm-publish flow requires a `token` attribute, even in dry-run mode. There is no token, so remove the step for now. --- .github/workflows/wasm.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index eef0e2c1..8a0d8619 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -12,9 +12,3 @@ jobs: run: docker-compose up - name: Run tests run: node test/wasm/test.js - - name: Publish to npm - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN }} - package: ./out/web/package.json - dry-run: true -- cgit v1.2.3 From 6b073f8992c3aa4c68045234f11e560045709125 Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 3 Nov 2021 13:13:15 -0400 Subject: Run the wasm build on push and pull_request (#4614) --- .github/workflows/wasm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 8a0d8619..d9a9c5cb 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -1,6 +1,6 @@ name: Wasm Build -on: push +on: [ push, pull_request ] jobs: build: -- cgit v1.2.3 From 1589720e1065bd163fb8e812f268413b13755f7c Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Fri, 5 Nov 2021 11:05:36 -0400 Subject: spirv-opt: create OpDecorate for OpMemberDecorate in desc-sroa (#4617) The scalar replacement of a resource array/struct variable must create OpDecorate for elements if OpMemberDecorate instructions decorate the elements. --- source/opt/desc_sroa.cpp | 105 ++++++++++++++++++++++++++++++++------------ source/opt/desc_sroa.h | 40 +++++++++++++++++ test/opt/desc_sroa_test.cpp | 63 ++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 27 deletions(-) diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index 0b839370..bcbdde94 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp @@ -19,6 +19,14 @@ namespace spvtools { namespace opt { +namespace { + +bool IsDecorationBinding(Instruction* inst) { + if (inst->opcode() != SpvOpDecorate) return false; + return inst->GetSingleWordInOperand(1u) == SpvDecorationBinding; +} + +} // namespace Pass::Status DescriptorScalarReplacement::Process() { bool modified = false; @@ -157,6 +165,74 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, return replacement_vars->second[idx]; } +void DescriptorScalarReplacement::CopyDecorationsForNewVariable( + Instruction* old_var, uint32_t index, uint32_t new_var_id, + uint32_t new_var_ptr_type_id, const bool is_old_var_array, + const bool is_old_var_struct, Instruction* old_var_type) { + // Handle OpDecorate instructions. + for (auto old_decoration : + get_decoration_mgr()->GetDecorationsFor(old_var->result_id(), true)) { + uint32_t new_binding = 0; + if (IsDecorationBinding(old_decoration)) { + new_binding = GetNewBindingForElement( + old_decoration->GetSingleWordInOperand(2), index, new_var_ptr_type_id, + is_old_var_array, is_old_var_struct, old_var_type); + } + CreateNewDecorationForNewVariable(old_decoration, new_var_id, new_binding); + } + + // Handle OpMemberDecorate instructions. + for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor( + old_var_type->result_id(), true)) { + assert(old_decoration->opcode() == SpvOpMemberDecorate); + if (old_decoration->GetSingleWordInOperand(1u) != index) continue; + CreateNewDecorationForMemberDecorate(old_decoration, new_var_id); + } +} + +uint32_t DescriptorScalarReplacement::GetNewBindingForElement( + uint32_t old_binding, uint32_t index, uint32_t new_var_ptr_type_id, + const bool is_old_var_array, const bool is_old_var_struct, + Instruction* old_var_type) { + if (is_old_var_array) { + return old_binding + index * GetNumBindingsUsedByType(new_var_ptr_type_id); + } + if (is_old_var_struct) { + // The binding offset that should be added is the sum of binding + // numbers used by previous members of the current struct. + uint32_t new_binding = old_binding; + for (uint32_t i = 0; i < index; ++i) { + new_binding += + GetNumBindingsUsedByType(old_var_type->GetSingleWordInOperand(i)); + } + return new_binding; + } + return old_binding; +} + +void DescriptorScalarReplacement::CreateNewDecorationForNewVariable( + Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) { + assert(old_decoration->opcode() == SpvOpDecorate); + std::unique_ptr new_decoration(old_decoration->Clone(context())); + new_decoration->SetInOperand(0, {new_var_id}); + + if (IsDecorationBinding(new_decoration.get())) { + new_decoration->SetInOperand(2, {new_binding}); + } + context()->AddAnnotationInst(std::move(new_decoration)); +} + +void DescriptorScalarReplacement::CreateNewDecorationForMemberDecorate( + Instruction* old_member_decoration, uint32_t new_var_id) { + std::vector operands( + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {new_var_id}}}); + auto new_decorate_operand_begin = old_member_decoration->begin() + 2u; + auto new_decorate_operand_end = old_member_decoration->end(); + operands.insert(operands.end(), new_decorate_operand_begin, + new_decorate_operand_end); + get_decoration_mgr()->AddDecoration(SpvOpDecorate, std::move(operands)); +} + uint32_t DescriptorScalarReplacement::CreateReplacementVariable( Instruction* var, uint32_t idx) { // The storage class for the new variable is the same as the original. @@ -192,33 +268,8 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable( {static_cast(storage_class)}}})); context()->AddGlobalValue(std::move(variable)); - // Copy all of the decorations to the new variable. The only difference is - // the Binding decoration needs to be adjusted. - for (auto old_decoration : - get_decoration_mgr()->GetDecorationsFor(var->result_id(), true)) { - assert(old_decoration->opcode() == SpvOpDecorate); - std::unique_ptr new_decoration( - old_decoration->Clone(context())); - new_decoration->SetInOperand(0, {id}); - - uint32_t decoration = new_decoration->GetSingleWordInOperand(1u); - if (decoration == SpvDecorationBinding) { - uint32_t new_binding = new_decoration->GetSingleWordInOperand(2); - if (is_array) { - new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id); - } - if (is_struct) { - // The binding offset that should be added is the sum of binding numbers - // used by previous members of the current struct. - for (uint32_t i = 0; i < idx; ++i) { - new_binding += GetNumBindingsUsedByType( - pointee_type_inst->GetSingleWordInOperand(i)); - } - } - new_decoration->SetInOperand(2, {new_binding}); - } - context()->AddAnnotationInst(std::move(new_decoration)); - } + CopyDecorationsForNewVariable(var, idx, id, ptr_element_type_id, is_array, + is_struct, pointee_type_inst); // Create a new OpName for the replacement variable. std::vector> names_to_add; diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index 70fd381a..fea06255 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h @@ -89,6 +89,46 @@ class DescriptorScalarReplacement : public Pass { // bindings used by its members. uint32_t GetNumBindingsUsedByType(uint32_t type_id); + // Copy all of the decorations of variable |old_var| and make them as + // decorations for the new variable whose id is |new_var_id|. The new variable + // is supposed to replace |index|th element of |old_var|. + // |new_var_ptr_type_id| is the id of the pointer to the type of the new + // variable. |is_old_var_array| is true if |old_var| has an array type. + // |is_old_var_struct| is true if |old_var| has a structure type. + // |old_var_type| is the pointee type of |old_var|. + void CopyDecorationsForNewVariable(Instruction* old_var, uint32_t index, + uint32_t new_var_id, + uint32_t new_var_ptr_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Get the new binding number for a new variable that will be replaced with an + // |index|th element of an old variable. The old variable has |old_binding| + // as its binding number. |ptr_elem_type_id| the id of the pointer to the + // element type. |is_old_var_array| is true if the old variable has an array + // type. |is_old_var_struct| is true if the old variable has a structure type. + // |old_var_type| is the pointee type of the old variable. + uint32_t GetNewBindingForElement(uint32_t old_binding, uint32_t index, + uint32_t ptr_elem_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Create a new OpDecorate instruction by cloning |old_decoration|. The new + // OpDecorate instruction will be used for a variable whose id is + // |new_var_ptr_type_id|. If |old_decoration| is a decoration for a binding, + // the new OpDecorate instruction will have |new_binding| as its binding. + void CreateNewDecorationForNewVariable(Instruction* old_decoration, + uint32_t new_var_id, + uint32_t new_binding); + + // Create a new OpDecorate instruction whose operand is the same as an + // OpMemberDecorate instruction |old_member_decoration| except Target operand. + // The Target operand of the new OpDecorate instruction will be |new_var_id|. + void CreateNewDecorationForMemberDecorate(Instruction* old_decoration, + uint32_t new_var_id); + // A map from an OpVariable instruction to the set of variables that will be // used to replace it. The entry |replacement_variables_[var][i]| is the id of // a variable that will be used in the place of the the ith element of the diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp index b35ad474..dcb625d6 100644 --- a/test/opt/desc_sroa_test.cpp +++ b/test/opt/desc_sroa_test.cpp @@ -770,6 +770,69 @@ TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) { SinglePassRunAndMatch(shader, true); } +TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) { + // Check that an OpMemberDecorate instruction is correctly converted to a + // OpDecorate instruction. + + const std::string shader = R"( +; CHECK: OpDecorate [[t:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[t]] Binding 0 +; CHECK: OpDecorate [[t]] RelaxedPrecision +; CHECK: OpDecorate [[s:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[s]] Binding 1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_TEXCOORD %out_var_SV_Target + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 600 + OpName %sampler2D_h "sampler2D_h" + OpMemberName %sampler2D_h 0 "t" + OpMemberName %sampler2D_h 1 "s" + OpName %type_2d_image "type.2d.image" + OpName %type_sampler "type.sampler" + OpName %_MainTex "_MainTex" + OpName %in_var_TEXCOORD "in.var.TEXCOORD" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %PSMain "PSMain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %_MainTex DescriptorSet 0 + OpDecorate %_MainTex Binding 0 + OpMemberDecorate %sampler2D_h 0 RelaxedPrecision + OpDecorate %out_var_SV_Target RelaxedPrecision + OpDecorate %69 RelaxedPrecision + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%type_sampler = OpTypeSampler +%sampler2D_h = OpTypeStruct %type_2d_image %type_sampler +%_ptr_UniformConstant_sampler2D_h = OpTypePointer UniformConstant %sampler2D_h + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %35 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + %_MainTex = OpVariable %_ptr_UniformConstant_sampler2D_h UniformConstant +%in_var_TEXCOORD = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %35 + %43 = OpLabel + %44 = OpLoad %v2float %in_var_TEXCOORD + %57 = OpLoad %sampler2D_h %_MainTex + %72 = OpCompositeExtract %type_2d_image %57 0 + %73 = OpCompositeExtract %type_sampler %57 1 + %68 = OpSampledImage %type_sampled_image %72 %73 + %69 = OpImageSampleImplicitLod %v4float %68 %44 None + OpStore %out_var_SV_Target %69 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(shader, true); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From 339d4475c1a806c187c57678af26733575d1cecd Mon Sep 17 00:00:00 2001 From: alan-baker Date: Fri, 5 Nov 2021 13:18:19 -0400 Subject: Improve decoration validation (#4490) Fixes #4469 * Checks that decorations only usable with structure members are not used by OpDecorate or OpDecorateId * Checks that decorations not allowed on structure members are not used with OpMemberDecorate * Checks decoration targets for most core decorations * Performs some Vulkan specific validation on deorations --- source/val/validate_annotation.cpp | 261 +++++++++- source/val/validate_builtins.cpp | 8 - source/val/validation_state.cpp | 2 + test/opt/inline_test.cpp | 2 - test/opt/upgrade_memory_model_test.cpp | 2 +- test/val/CMakeLists.txt | 1 + test/val/val_annotation_test.cpp | 917 +++++++++++++++++++++++++++++++++ test/val/val_builtins_test.cpp | 82 +-- test/val/val_capability_test.cpp | 466 ++++++++++------- test/val/val_decoration_test.cpp | 42 +- test/val/val_id_test.cpp | 14 +- test/val/val_interfaces_test.cpp | 7 +- test/val/val_layout_test.cpp | 2 - test/val/val_memory_test.cpp | 10 +- 14 files changed, 1486 insertions(+), 330 deletions(-) create mode 100644 test/val/val_annotation_test.cpp diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index 85d2b751..16d44906 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -138,14 +138,14 @@ std::string LogStringForDecoration(uint32_t decoration) { return "PerTaskNV"; case SpvDecorationPerVertexNV: return "PerVertexNV"; - case SpvDecorationNonUniformEXT: - return "NonUniformEXT"; - case SpvDecorationRestrictPointerEXT: - return "RestrictPointerEXT"; - case SpvDecorationAliasedPointerEXT: - return "AliasedPointerEXT"; - case SpvDecorationHlslCounterBufferGOOGLE: - return "HlslCounterBufferGOOGLE"; + case SpvDecorationNonUniform: + return "NonUniform"; + case SpvDecorationRestrictPointer: + return "RestrictPointer"; + case SpvDecorationAliasedPointer: + return "AliasedPointer"; + case SpvDecorationCounterBuffer: + return "CounterBuffer"; case SpvDecorationHlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; default: @@ -156,8 +156,8 @@ std::string LogStringForDecoration(uint32_t decoration) { // Returns true if the decoration takes ID parameters. // TODO(dneto): This can be generated from the grammar. -bool DecorationTakesIdParameters(uint32_t type) { - switch (static_cast(type)) { +bool DecorationTakesIdParameters(SpvDecoration type) { + switch (type) { case SpvDecorationUniformId: case SpvDecorationAlignmentId: case SpvDecorationMaxByteOffsetId: @@ -169,17 +169,212 @@ bool DecorationTakesIdParameters(uint32_t type) { return false; } -spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { - const auto decoration = inst->GetOperandAs(1); - if (decoration == SpvDecorationSpecId) { - const auto target_id = inst->GetOperandAs(0); - const auto target = _.FindDef(target_id); - if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpDecorate SpecId decoration target '" - << _.getIdName(target_id) - << "' is not a scalar specialization constant."; +bool IsMemberDecorationOnly(SpvDecoration dec) { + switch (dec) { + case SpvDecorationRowMajor: + case SpvDecorationColMajor: + case SpvDecorationMatrixStride: + // SPIR-V spec bug? Offset is generated on variables when dealing with + // transform feedback. + // case SpvDecorationOffset: + return true; + default: + break; + } + return false; +} + +bool IsNotMemberDecoration(SpvDecoration dec) { + switch (dec) { + case SpvDecorationSpecId: + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationArrayStride: + case SpvDecorationGLSLShared: + case SpvDecorationGLSLPacked: + case SpvDecorationCPacked: + // TODO: https://github.com/KhronosGroup/glslang/issues/703: + // glslang applies Restrict to structure members. + // case SpvDecorationRestrict: + case SpvDecorationAliased: + case SpvDecorationConstant: + case SpvDecorationUniform: + case SpvDecorationUniformId: + case SpvDecorationSaturatedConversion: + case SpvDecorationIndex: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationFuncParamAttr: + case SpvDecorationFPRoundingMode: + case SpvDecorationFPFastMathMode: + case SpvDecorationLinkageAttributes: + case SpvDecorationNoContraction: + case SpvDecorationInputAttachmentIndex: + case SpvDecorationAlignment: + case SpvDecorationMaxByteOffset: + case SpvDecorationAlignmentId: + case SpvDecorationMaxByteOffsetId: + case SpvDecorationNoSignedWrap: + case SpvDecorationNoUnsignedWrap: + case SpvDecorationNonUniform: + case SpvDecorationRestrictPointer: + case SpvDecorationAliasedPointer: + case SpvDecorationCounterBuffer: + return true; + default: + break; + } + return false; +} + +spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, + const Instruction* inst, + const Instruction* target) { + auto fail = [&_, dec, inst, target](uint32_t vuid = 0) -> DiagnosticStream { + DiagnosticStream ds = std::move( + _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(vuid) << LogStringForDecoration(dec) + << " decoration on target '" << _.getIdName(target->id()) << "' "); + return ds; + }; + switch (dec) { + case SpvDecorationSpecId: + if (!spvOpcodeIsScalarSpecConstant(target->opcode())) { + return fail() << "must be a scalar specialization constant"; + } + break; + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationGLSLShared: + case SpvDecorationGLSLPacked: + case SpvDecorationCPacked: + if (target->opcode() != SpvOpTypeStruct) { + return fail() << "must be a structure type"; + } + break; + case SpvDecorationArrayStride: + if (target->opcode() != SpvOpTypeArray && + target->opcode() != SpvOpTypeRuntimeArray && + target->opcode() != SpvOpTypePointer) { + return fail() << "must be an array or pointer type"; + } + break; + case SpvDecorationBuiltIn: + if (target->opcode() != SpvOpVariable && + !spvOpcodeIsConstant(target->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "BuiltIns can only target variables, structure members or " + "constants"; + } + if (inst->GetOperandAs(2) == SpvBuiltInWorkgroupSize) { + if (!spvOpcodeIsConstant(target->opcode())) { + return fail() << "must be a constant for WorkgroupSize"; + } + } else if (target->opcode() != SpvOpVariable) { + return fail() << "must be a variable"; + } + break; + case SpvDecorationNoPerspective: + case SpvDecorationFlat: + case SpvDecorationPatch: + case SpvDecorationCentroid: + case SpvDecorationSample: + case SpvDecorationRestrict: + case SpvDecorationAliased: + case SpvDecorationVolatile: + case SpvDecorationCoherent: + case SpvDecorationNonWritable: + case SpvDecorationNonReadable: + case SpvDecorationXfbBuffer: + case SpvDecorationXfbStride: + case SpvDecorationComponent: + case SpvDecorationStream: + case SpvDecorationRestrictPointer: + case SpvDecorationAliasedPointer: + if (target->opcode() != SpvOpVariable && + target->opcode() != SpvOpFunctionParameter) { + return fail() << "must be a memory object declaration"; + } + if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) { + return fail() << "must be a pointer type"; + } + break; + case SpvDecorationInvariant: + case SpvDecorationConstant: + case SpvDecorationLocation: + case SpvDecorationIndex: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationInputAttachmentIndex: + if (target->opcode() != SpvOpVariable) { + return fail() << "must be a variable"; + } + break; + default: + break; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + // The following were all checked as pointer types above. + SpvStorageClass sc = SpvStorageClassUniform; + const auto type = _.FindDef(target->type_id()); + if (type && type->operands().size() > 2) { + sc = type->GetOperandAs(1); } + switch (dec) { + case SpvDecorationLocation: + case SpvDecorationComponent: + // Location is used for input, output and ray tracing stages. + if (sc == SpvStorageClassStorageBuffer || + sc == SpvStorageClassUniform || + sc == SpvStorageClassUniformConstant || + sc == SpvStorageClassWorkgroup || sc == SpvStorageClassPrivate || + sc == SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_ID, target) + << LogStringForDecoration(dec) + << " decoration must not be applied to this storage class"; + } + break; + case SpvDecorationIndex: + if (sc != SpvStorageClassOutput) { + return fail() << "must be in the Output storage class"; + } + break; + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + if (sc != SpvStorageClassStorageBuffer && + sc != SpvStorageClassUniform && + sc != SpvStorageClassUniformConstant) { + return fail() << "must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class"; + } + break; + case SpvDecorationInputAttachmentIndex: + if (sc != SpvStorageClassUniformConstant) { + return fail() << "must be in the UniformConstant storage class"; + } + break; + case SpvDecorationFlat: + case SpvDecorationNoPerspective: + case SpvDecorationCentroid: + case SpvDecorationSample: + if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput) { + return fail(4670) << "storage class must be Input or Output"; + } + break; + default: + break; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { + const auto decoration = inst->GetOperandAs(1); + const auto target_id = inst->GetOperandAs(0); + const auto target = _.FindDef(target_id); + if (!target) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined"; } if (spvIsVulkanEnv(_.context()->target_env)) { @@ -197,17 +392,34 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { << "Decorations taking ID parameters may not be used with " "OpDecorateId"; } + + if (target->opcode() != SpvOpDecorationGroup) { + if (IsMemberDecorationOnly(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << LogStringForDecoration(decoration) + << " can only be applied to structure members"; + } + + if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) { + return error; + } + } + // TODO: Add validations for all decorations. return SPV_SUCCESS; } spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) { - const auto decoration = inst->GetOperandAs(1); + const auto decoration = inst->GetOperandAs(1); if (!DecorationTakesIdParameters(decoration)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Decorations that don't take ID parameters may not be used with " "OpDecorateId"; } + + // No member decorations take id parameters, so we don't bother checking if + // we are using a member only decoration here. + // TODO: Add validations for these decorations. // UniformId is covered elsewhere. return SPV_SUCCESS; @@ -234,6 +446,13 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _, << " members. Largest valid index is " << member_count - 1 << "."; } + const auto decoration = inst->GetOperandAs(2); + if (IsNotMemberDecoration(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << LogStringForDecoration(decoration) + << " cannot be applied to structure members"; + } + return SPV_SUCCESS; } diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index a6e624f7..57dde8ad 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp @@ -3993,14 +3993,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( const Decoration& decoration, const Instruction& inst) { const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); - // Builtins can only be applied to variables, structures or constants. - auto target_opcode = inst.opcode(); - if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable && - !spvOpcodeIsConstant(target_opcode)) { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << "BuiltIns can only target variables, structs or constants"; - } - if (!spvIsVulkanEnv(_.context()->target_env)) { // Early return. All currently implemented rules are based on Vulkan spec. // diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 79638727..8d1a0d3f 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -1839,6 +1839,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-None-04667); case 4669: return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); + case 4670: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-04670); case 4675: return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); case 4677: diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index 6b48f280..d22f027c 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -2300,7 +2300,6 @@ TEST_F(InlineTest, DontInlineDirectlyRecursiveFunc) { OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "main" OpExecutionMode %1 OriginUpperLeft -OpDecorate %2 DescriptorSet 439418829 %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2330,7 +2329,6 @@ TEST_F(InlineTest, DontInlineInDirectlyRecursiveFunc) { OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "main" OpExecutionMode %1 OriginUpperLeft -OpDecorate %2 DescriptorSet 439418829 %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp index 7f64ffd7..2cd3c7df 100644 --- a/test/opt/upgrade_memory_model_test.cpp +++ b/test/opt/upgrade_memory_model_test.cpp @@ -404,7 +404,7 @@ OpCapability VariablePointers OpExtension "SPV_KHR_variable_pointers" OpMemoryModel Logical GLSL450 OpDecorate %param Coherent -OpDecorate %param ArrayStride 4 +OpDecorate %ptr_int_StorageBuffer ArrayStride 4 %void = OpTypeVoid %bool = OpTypeBool %int = OpTypeInt 32 0 diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 39f9a098..64eba446 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -23,6 +23,7 @@ set(VAL_TEST_COMMON_SRCS add_spvtools_unittest(TARGET val_abcde SRCS val_adjacency_test.cpp + val_annotation_test.cpp val_arithmetics_test.cpp val_atomics_test.cpp val_barriers_test.cpp diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp new file mode 100644 index 00000000..cd560057 --- /dev/null +++ b/test/val/val_annotation_test.cpp @@ -0,0 +1,917 @@ +// Copyright (c) 2021 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. + +// Validation tests for decorations + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Values; + +using MemberOnlyDecorations = spvtest::ValidateBase; + +TEST_P(MemberOnlyDecorations, MemberDecoration) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 )" + + deco + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemberOnlyDecorations, Decoration) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can only be applied to structure members")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateMemberOnlyDecorations, MemberOnlyDecorations, + Values("RowMajor", "ColMajor", "MatrixStride 16" + // SPIR-V spec bug? + /*,"Offset 0"*/)); + +using NonMemberOnlyDecorations = spvtest::ValidateBase; + +TEST_P(NonMemberOnlyDecorations, MemberDecoration) { + const auto deco = GetParam(); + const auto text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpCapability Addresses +OpCapability PhysicalStorageBufferAddresses +OpCapability ShaderNonUniform +OpExtension "SPV_KHR_no_integer_wrap_decoration" +OpExtension "SPV_KHR_physical_storage_buffer" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 )" + + deco + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("cannot be applied to structure members")); +} + +INSTANTIATE_TEST_SUITE_P( + ValidateNonMemberOnlyDecorations, NonMemberOnlyDecorations, + Values("SpecId 1", "Block", "BufferBlock", "ArrayStride 4", "GLSLShared", + "GLSLPacked", "CPacked", + // TODO: https://github.com/KhronosGroup/glslang/issues/703: + // glslang applies Restrict to structure members. + //"Restrict", + "Aliased", "Constant", "Uniform", "SaturatedConversion", "Index 0", + "Binding 0", "DescriptorSet 0", "FuncParamAttr Zext", + "FPRoundingMode RTE", "FPFastMathMode None", + "LinkageAttributes \"ext\" Import", "NoContraction", + "InputAttachmentIndex 0", "Alignment 4", "MaxByteOffset 4", + "AlignmentId %float", "MaxByteOffsetId %float", "NoSignedWrap", + "NoUnsignedWrap", "NonUniform", "RestrictPointer", "AliasedPointer", + "CounterBuffer %float")); + +using StructDecorations = spvtest::ValidateBase; + +TEST_P(StructDecorations, Struct) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%struct = OpTypeStruct +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(StructDecorations, OtherType) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int )" + deco + R"( +%int = OpTypeInt 32 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, Variable) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Private %int +%var = OpVariable %ptr Private +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, Constant) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int_0 )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateStructDecorations, StructDecorations, + Values("Block", "BufferBlock", "GLSLShared", + "GLSLPacked", "CPacked")); + +using ArrayDecorations = spvtest::ValidateBase; + +TEST_P(ArrayDecorations, Array) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %array )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, RuntimeArray) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %array )" + deco + + R"( +%int = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, Pointer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ptr )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Workgroup %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, Struct) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Private %int +%var = OpVariable %ptr Private +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %null )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%null = OpConstantNull %array +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateArrayDecorations, ArrayDecorations, + Values("ArrayStride 4")); + +using BuiltInDecorations = spvtest::ValidateBase; + +TEST_P(BuiltInDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Input %int +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + if (deco != "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a constant for WorkgroupSize")); + } +} + +TEST_P(BuiltInDecorations, IntegerType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); +} + +TEST_P(BuiltInDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); +} + +TEST_P(BuiltInDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %const BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%const = OpConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + if (deco == "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); + } +} + +TEST_P(BuiltInDecorations, SpecConstant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %const BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%const = OpSpecConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + if (deco == "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); + } +} + +INSTANTIATE_TEST_SUITE_P(ValidateBuiltInDecorations, BuiltInDecorations, + Values("Position", "PointSize", "VertexId", + "InstanceId", "FragCoord", "FrontFacing", + "NumWorkgroups", "WorkgroupSize", + "LocalInvocationId", "GlobalInvocationId")); + +using MemoryObjectDecorations = spvtest::ValidateBase; + +TEST_P(MemoryObjectDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemoryObjectDecorations, FunctionParameterGood) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%void = OpTypeVoid +%fn = OpTypeFunction %void %ptr +%func = OpFunction %void None %fn +%param = OpFunctionParameter %ptr +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemoryObjectDecorations, FunctionParameterNotAPointer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%void = OpTypeVoid +%fn = OpTypeFunction %void %float +%func = OpFunction %void None %fn +%param = OpFunctionParameter %float +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); +} + +TEST_P(MemoryObjectDecorations, FloatType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %float )" + deco + + R"( +%float = OpTypeFloat 32 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a memory object declaration")); +} + +TEST_P(MemoryObjectDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %const )" + deco + + R"( +%float = OpTypeFloat 32 +%const = OpConstant %float 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a memory object declaration")); +} + +// NonWritable and NonReadable are covered by other tests. +INSTANTIATE_TEST_SUITE_P( + ValidateMemoryObjectDecorations, MemoryObjectDecorations, + Values("NoPerspective", "Flat", "Patch", "Centroid", "Component 0", + "Sample", "Restrict", "Aliased", "Volatile", "Coherent", "Stream 0", + "XfbBuffer 1", "XfbStride 1", "AliasedPointer", "RestrictPointer")); + +using VariableDecorations = spvtest::ValidateBase; + +TEST_P(VariableDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(VariableDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%void = OpTypeVoid +%fn = OpTypeFunction %void %float +%func = OpFunction %void None %fn +%param = OpFunctionParameter %float +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +TEST_P(VariableDecorations, FloatType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %float )" + deco + + R"( +%float = OpTypeFloat 32 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +TEST_P(VariableDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %const )" + deco + + R"( +%float = OpTypeFloat 32 +%const = OpConstant %float 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVariableDecorations, VariableDecorations, + Values("Invariant", "Constant", "Location 0", + "Index 0", "Binding 0", "DescriptorSet 0")); + +using VulkanIOStorageClass = + spvtest::ValidateBase>; + +TEST_P(VulkanIOStorageClass, Invalid) { + const auto deco = std::get<0>(GetParam()); + const auto sc = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%ptr = OpTypePointer )" + + sc + + R"( %float +%var = OpVariable %ptr )" + sc + + R"( +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decoration must not be applied to this storage class")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanIOStorageClass, VulkanIOStorageClass, + Combine(Values("Location", "Component"), + Values("StorageBuffer", "Uniform", + "UniformConstant", "Workgroup", + "Private"))); + +using VulkanResourceStorageClass = + spvtest::ValidateBase>; + +TEST_P(VulkanResourceStorageClass, Invalid) { + const auto deco = std::get<0>(GetParam()); + const auto sc = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%ptr = OpTypePointer )" + + sc + + R"( %float +%var = OpVariable %ptr )" + sc + + R"( +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanResourceStorageClass, + VulkanResourceStorageClass, + Combine(Values("DescriptorSet", "Binding"), + Values("Private", "Input", "Output", + "Workgroup"))); + +using VulkanInterpolationStorageClass = spvtest::ValidateBase; + +TEST_P(VulkanInterpolationStorageClass, Input) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_P(VulkanInterpolationStorageClass, Output) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Output %float +%var = OpVariable %ptr Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_P(VulkanInterpolationStorageClass, Private) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Private %float +%var = OpVariable %ptr Private +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +TEST_P(VulkanInterpolationStorageClass, Uniform) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +OpDecorate %var Binding 0 +OpDecorate %var DescriptorSet 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Uniform %float +%var = OpVariable %ptr Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +TEST_P(VulkanInterpolationStorageClass, StorageBuffer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +OpDecorate %var Binding 0 +OpDecorate %var DescriptorSet 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer StorageBuffer %float +%var = OpVariable %ptr StorageBuffer +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanInterpolationStorageClass, + VulkanInterpolationStorageClass, + Values("Flat", "NoPerspective", "Centroid", "Sample")); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index 84952645..dff9adfe 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -2861,9 +2861,9 @@ OpDecorate %copy BuiltIn WorkgroupSize CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("BuiltIns can only target variables, structs or constants")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure " + "members or constants")); } CodeGenerator GetWorkgroupSizeNotVectorGenerator() { @@ -3482,35 +3482,6 @@ OpDecorate %gl_ViewportIndex PerPrimitiveNV EXPECT_THAT(getDiagnosticString(), HasSubstr("is not an int scalar")); } -TEST_F(ValidateBuiltIns, GetUnderlyingTypeNoAssert) { - std::string spirv = R"( - OpCapability Shader - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "PSMa" %12 %17 - OpExecutionMode %4 OriginUpperLeft - OpDecorate %gl_PointCoord BuiltIn PointCoord - OpDecorate %12 Location 0 - OpDecorate %17 Location 0 - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 - %gl_PointCoord = OpTypeStruct %v4float - %_ptr_Input_v4float = OpTypePointer Input %v4float - %_ptr_Output_v4float = OpTypePointer Output %v4float - %12 = OpVariable %_ptr_Input_v4float Input - %17 = OpVariable %_ptr_Output_v4float Output - %4 = OpFunction %void None %3 - %15 = OpLabel - OpReturn - OpFunctionEnd)"; - CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("did not find an member index to get underlying data " - "type")); -} - TEST_P(ValidateVulkanSubgroupBuiltIns, InMain) { const char* const built_in = std::get<0>(GetParam()); const char* const execution_model = std::get<1>(GetParam()); @@ -3781,9 +3752,9 @@ OpDecorate %void BuiltIn Position CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("BuiltIns can only target variables, structs or constants")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); } TEST_F(ValidateBuiltIns, TargetIsVariable) { @@ -3801,47 +3772,6 @@ OpDecorate %wg_var BuiltIn Position EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateBuiltIns, TargetIsStruct) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %struct BuiltIn Position -%struct = OpTypeStruct -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateBuiltIns, TargetIsConstant) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %int0 BuiltIn Position -%int = OpTypeInt 32 0 -%int0 = OpConstant %int 0 -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateBuiltIns, TargetIsSpecConstant) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %int0 BuiltIn Position -%int = OpTypeInt 32 0 -%int0 = OpSpecConstant %int 0 -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - INSTANTIATE_TEST_SUITE_P( PrimitiveShadingRateOutputSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 82f8d381..ffd9e0ae 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -1205,8 +1205,10 @@ INSTANTIATE_TEST_SUITE_P(Decoration, ValidateCapability, Values( std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt RelaxedPrecision\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var RelaxedPrecision\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Private %intt\n" + "%var = OpVariable %ptr Private\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + // Block applies to struct type. @@ -1224,93 +1226,125 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt RowMajor\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 RowMajor\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt ColMajor\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 ColMajor\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt ArrayStride 1\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %array ArrayStride 4\n" + "%intt = OpTypeInt 32 0\n" + "%array = OpTypeRuntimeArray %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt MatrixStride 1\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 MatrixStride 8\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt GLSLShared\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct GLSLShared\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt GLSLPacked\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct GLSLPacked\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" - "OpDecorate %intt CPacked\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct CPacked\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt NoPerspective\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var NoPerspective\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Flat\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Flat\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Patch\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Patch\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Centroid\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Centroid\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Sample\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Sample\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Invariant\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Invariant\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Restrict\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Restrict\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Aliased\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Aliased\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Volatile\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Volatile\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" - "OpDecorate %intt Constant\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Constant\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Coherent\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Coherent\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + // NonWritable must target something valid, such as a storage image. @@ -1324,8 +1358,12 @@ std::make_pair(std::string(kOpenCLMemoryModel) + AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt NonReadable\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var NonReadable " + "%float = OpTypeFloat 32 " + "%imstor = OpTypeImage %float 2D 0 0 0 2 Unknown " + "%ptr = OpTypePointer UniformConstant %imstor " + "%var = OpVariable %ptr UniformConstant " + + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + // Uniform must target a non-void value. @@ -1342,8 +1380,10 @@ std::make_pair(std::string(kGLSL450MemoryModel) + KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Stream 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Stream 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Output %intt\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), std::vector{"GeometryStreams"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" @@ -1360,33 +1400,44 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Index 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Index 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Binding 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Binding 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt DescriptorSet 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var DescriptorSet 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Offset 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 Offset 0\n" + "%intt = OpTypeInt 32 0\n" + "%structt = OpTypeStruct %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt XfbBuffer 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var XfbBuffer 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), std::vector{"TransformFeedback"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt XfbStride 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var XfbStride 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), std::vector{"TransformFeedback"}), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" @@ -1410,8 +1461,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt InputAttachmentIndex 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var InputAttachmentIndex 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer UniformConstant %intt\n" + "%var = OpVariable %ptr UniformConstant\n" + std::string(kVoidFVoid), std::vector{"InputAttachment"}), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" @@ -1471,264 +1524,308 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapability, Values( std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn Position\n" + "OpDecorate %var BuiltIn Position\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), // Just mentioning PointSize, ClipDistance, or CullDistance as a BuiltIn does // not trigger the requirement for the associated capability. // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" + "OpDecorate %var BuiltIn PointSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn ClipDistance\n" + "OpDecorate %var BuiltIn ClipDistance\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" + "OpDecorate %var BuiltIn CullDistance\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexId\n" + "OpDecorate %var BuiltIn VertexId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceId\n" + "OpDecorate %var BuiltIn InstanceId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PrimitiveId\n" + "OpDecorate %var BuiltIn PrimitiveId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryTessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InvocationId\n" + "OpDecorate %var BuiltIn InvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryTessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn Layer\n" + "OpDecorate %var BuiltIn Layer\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn ViewportIndex\n" + "OpDecorate %var BuiltIn ViewportIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"MultiViewport"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessLevelOuter\n" + "OpDecorate %var BuiltIn TessLevelOuter\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessLevelInner\n" + "OpDecorate %var BuiltIn TessLevelInner\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessCoord\n" + "OpDecorate %var BuiltIn TessCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PatchVertices\n" + "OpDecorate %var BuiltIn PatchVertices\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FragCoord\n" + "OpDecorate %var BuiltIn FragCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PointCoord\n" + "OpDecorate %var BuiltIn PointCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FrontFacing\n" + "OpDecorate %var BuiltIn FrontFacing\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SampleId\n" + "OpDecorate %var BuiltIn SampleId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SamplePosition\n" + "OpDecorate %var BuiltIn SamplePosition\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SampleMask\n" + "OpDecorate %var BuiltIn SampleMask\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FragDepth\n" + "OpDecorate %var BuiltIn FragDepth\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn HelperInvocation\n" + "OpDecorate %var BuiltIn HelperInvocation\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexIndex\n" + "OpDecorate %var BuiltIn VertexIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceIndex\n" + "OpDecorate %var BuiltIn InstanceIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn NumWorkgroups\n" + "OpDecorate %var BuiltIn NumWorkgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn WorkgroupSize\n" + "OpDecorate %ones BuiltIn WorkgroupSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%int3 = OpTypeVector %intt 3\n" + "%int_1 = OpConstant %intt 1\n" + "%ones = OpConstantComposite %int3 %int_1 %int_1 %int_1\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn WorkgroupId\n" + "OpDecorate %var BuiltIn WorkgroupId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn LocalInvocationId\n" + "OpDecorate %var BuiltIn LocalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn GlobalInvocationId\n" + "OpDecorate %var BuiltIn GlobalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn LocalInvocationIndex\n" + "OpDecorate %var BuiltIn LocalInvocationIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn WorkDim\n" + "OpDecorate %var BuiltIn WorkDim\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalSize\n" + "OpDecorate %var BuiltIn GlobalSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn EnqueuedWorkgroupSize\n" + "OpDecorate %var BuiltIn EnqueuedWorkgroupSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalOffset\n" + "OpDecorate %var BuiltIn GlobalOffset\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalLinearId\n" + "OpDecorate %var BuiltIn GlobalLinearId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupSize\n" + "OpDecorate %var BuiltIn SubgroupSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupMaxSize\n" + "OpDecorate %var BuiltIn SubgroupMaxSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn NumSubgroups\n" + "OpDecorate %var BuiltIn NumSubgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn NumEnqueuedSubgroups\n" + "OpDecorate %var BuiltIn NumEnqueuedSubgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupId\n" + "OpDecorate %var BuiltIn SubgroupId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupLocalInvocationId\n" + "OpDecorate %var BuiltIn SubgroupLocalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexIndex\n" + "OpDecorate %var BuiltIn VertexIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceIndex\n" + "OpDecorate %var BuiltIn InstanceIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()) ))); @@ -1742,11 +1839,11 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityVulkan10, ValuesIn(AllSpirV10Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" - "OpMemberDecorate %block 0 BuiltIn PointSize\n" - "%f32 = OpTypeFloat 32\n" - "%block = OpTypeStruct %f32\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), // Capabilities which should succeed. AllVulkan10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + @@ -1775,22 +1872,31 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityOpenGL40, ValuesIn(AllSpirV10Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn ClipDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn ClipDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()) ))); @@ -1800,16 +1906,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan11, ValuesIn(AllCapabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllVulkan11Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllVulkan11Capabilities()) ))); @@ -1819,16 +1930,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan12, ValuesIn(AllSpirV15Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllVulkan12Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllVulkan12Capabilities()) ))); diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index 508acfa1..f2953edc 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -687,8 +687,7 @@ TEST_F(ValidateDecorations, BlockDecoratingArrayBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, BlockDecoratingIntBad) { @@ -713,8 +712,7 @@ TEST_F(ValidateDecorations, BlockDecoratingIntBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, BlockMissingOffsetBad) { @@ -6129,9 +6127,7 @@ TEST_F(ValidateDecorations, NonWritableLabelTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %label = OpLabel")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { @@ -6140,9 +6136,7 @@ TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %void = OpTypeVoid")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableValueTargetBad) { @@ -6151,9 +6145,7 @@ TEST_F(ValidateDecorations, NonWritableValueTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %float_0 = OpConstant %float 0")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableValueParamBad) { @@ -6161,10 +6153,7 @@ TEST_F(ValidateDecorations, NonWritableValueParamBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " - "buffer\n %param_f = OpFunctionParameter %float")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); } TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) { @@ -6467,8 +6456,7 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of Component decoration must be " - "a memory object declaration")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) { @@ -6767,8 +6755,8 @@ TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) { )"; CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), Eq("")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); } TEST_F(ValidateDecorations, VulkanStorageBufferBlock) { @@ -7164,9 +7152,7 @@ OpDecorate %struct Location 0 CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Location decoration can only be applied to a variable " - "or member of a structure type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); } TEST_F(ValidateDecorations, LocationFloatBad) { @@ -7180,9 +7166,7 @@ OpDecorate %float Location 0 CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Location decoration can only be applied to a variable " - "or member of a structure type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); } TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) { @@ -7571,9 +7555,7 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableNotAStruct) { CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) { diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index dd4c952c..ac057493 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -5524,9 +5524,9 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target " - "'1[%uint_3]' is not a scalar specialization " - "constant.")); + HasSubstr("SpecId decoration on target " + "'1[%uint_3]' must be a scalar specialization " + "constant")); } TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) { @@ -5546,8 +5546,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " - "not a scalar specialization constant.")); + HasSubstr("SpecId decoration on target '1[%1]' " + "must be a scalar specialization constant")); } TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) { @@ -5566,8 +5566,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " - "not a scalar specialization constant.")); + HasSubstr("SpecId decoration on target '1[%1]' " + "must be a scalar specialization constant")); } TEST_F(ValidateIdWithMessage, SpecIdTargetGood) { diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index 5ae19fa3..a01fc19b 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -1267,10 +1267,9 @@ OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Index can only be applied to Fragment output variables")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be in the Output storage class")); } TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponent) { diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp index d34c97f9..7ebd7c00 100644 --- a/test/val/val_layout_test.cpp +++ b/test/val/val_layout_test.cpp @@ -538,7 +538,6 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidIn10) { OpMemoryModel Logical GLSL450 OpName %void "void" OpModuleProcessed "this is ok in 1.1 and later" - OpDecorate %void Volatile ; bogus, but makes the example short %void = OpTypeVoid )"; @@ -558,7 +557,6 @@ TEST_F(ValidateLayout, ModuleProcessedValidIn11) { OpMemoryModel Logical GLSL450 OpName %void "void" OpModuleProcessed "this is ok in 1.1 and later" - OpDecorate %void Volatile ; bogus, but makes the example short %void = OpTypeVoid )"; diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index f17133cc..2a884c4d 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -2344,11 +2344,12 @@ OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t Block +OpDecorate %struct Block %uint_t = OpTypeInt 32 0 %inner_array_t = OpTypeRuntimeArray %uint_t %array_t = OpTypeRuntimeArray %inner_array_t -%array_ptr = OpTypePointer StorageBuffer %array_t +%struct = OpTypeStruct %array_t +%array_ptr = OpTypePointer StorageBuffer %struct %2 = OpVariable %array_ptr StorageBuffer %void = OpTypeVoid %func_t = OpTypeFunction %void @@ -2504,13 +2505,14 @@ OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t Block +OpDecorate %struct Block %uint_t = OpTypeInt 32 0 %dim = OpConstant %uint_t 1 %sampler_t = OpTypeSampler %inner_array_t = OpTypeRuntimeArray %uint_t %array_t = OpTypeRuntimeArray %inner_array_t -%array_ptr = OpTypePointer StorageBuffer %array_t +%struct = OpTypeStruct %array_t +%array_ptr = OpTypePointer StorageBuffer %struct %2 = OpVariable %array_ptr StorageBuffer %void = OpTypeVoid %func_t = OpTypeFunction %void -- cgit v1.2.3 From 2bd46c5c55b8c9c5842b25e2d98c1444f3d22b7d Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Mon, 8 Nov 2021 21:40:53 -0500 Subject: Update CHANGES --- CHANGES | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index c905ad49..4128db1b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,18 @@ Revision history for SPIRV-Tools -v2021.4-dev 2021-08-25 - - Add a WebAssembly build +v2021.4-dev 2021-11-09 + - General + - Add a WebAssembly build (#3752) + - Make cxx exceptions controllable (#4591) + - Validator + - Improve decoration validation (#4490) + - Optimizer + - Add spirv-opt pass to replace descriptor accesses based on variable indices (#4574) + - Do not fold snegate feeding sdiv (#4600) + - Handle overflowing id in merge return (#4606) + - Fuzzer + - Add libFuzzer target for spirv-fuzz (#4434) + - Linter v2021.3 2021-08-24 - General -- cgit v1.2.3 From 6f9fa480123cfb788637ff7c6daf5b1c9c1e408f Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Tue, 9 Nov 2021 09:43:45 -0500 Subject: Update DEPS (#4625) Update DEPS ahead of the next Vulkan SDK release. * Roll external/effcee/ 2ec8f8738..ddf5e2bb9 (1 commit) https://github.com/google/effcee/compare/2ec8f8738118...ddf5e2bb9295 $ git log 2ec8f8738..ddf5e2bb9 --date=short --no-merges --format='%ad %ae %s' 2021-08-31 dneto Remove .travis.yml Created with: roll-dep external/effcee * Roll external/googletest/ 955c7f837..bf0701daa (32 commits) https://github.com/google/googletest/compare/955c7f837efa...bf0701daa9f5 $ git log 955c7f837..bf0701daa --date=short --no-merges --format='%ad %ae %s' 2021-11-03 absl-team Googletest export 2021-10-28 dmauro Googletest export 2021-10-27 dmauro Googletest export 2021-10-26 absl-team Googletest export 2021-10-26 absl-team Googletest export 2021-10-25 dmauro Googletest export 2021-10-25 dmauro Googletest export 2021-10-30 dartme18 Re #3637 Show Disabled Tests in testing output 2021-10-11 absl-team Googletest export 2021-10-11 absl-team Googletest export 2021-10-11 absl-team Googletest export 2021-10-08 yesudeep Fix linker errors on FreeBSD. 2021-09-30 absl-team Googletest export 2021-09-28 absl-team Googletest export 2021-09-24 cclauss Fix remaining typos discovered by codespell 2021-09-20 absl-team Googletest export 2021-09-20 absl-team Internal change 2021-09-17 julian.amann Remove bazelbuild/rules_cc dependency 2021-09-16 cclauss Fix typos discovered by codespell 2021-09-16 cclauss Fix typos discovered by codespell 2021-09-16 cclauss Fix typo discovered by codespell 2021-09-15 absl-team Googletest export 2021-09-15 dmauro Googletest export 2021-09-15 absl-team Googletest export 2021-09-14 dmauro Googletest export 2021-09-14 dmauro Googletest export 2021-09-14 absl-team Googletest export 2021-09-15 761129+derekmauro Revert grammatically incorrect change 2021-09-10 absl-team Googletest export 2021-08-10 akashkumarsingh11032001 add a missing 'a' 2021-08-10 akashkumarsingh11032001 Create CONTRIBUTING.md 2020-12-29 julien.jemine Using auto instead of container::const_iterator Created with: roll-dep external/googletest * Roll external/spirv-headers/ 19e835041..29817199b (3 commits) https://github.com/KhronosGroup/SPIRV-Headers/compare/19e8350415ed...29817199b706 $ git log 19e835041..29817199b --date=short --no-merges --format='%ad %ae %s' 2021-07-24 kpet Add SpecConstantSubgroupMaxSize to the clspv reflection non-semantic instruction set 2021-05-24 pmistry Implement header definitions for SPV_NV_bindless_texture 2021-10-12 ben.ashbaugh reserve SPIR-V enum block for Intel extensions Created with: roll-dep external/spirv-headers --- DEPS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index bf768939..677244ed 100644 --- a/DEPS +++ b/DEPS @@ -3,10 +3,10 @@ use_relative_paths = True vars = { 'github': 'https://github.com', - 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', - 'googletest_revision': '955c7f837efad184ec63e771c42542d37545eaef', + 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133', + 'googletest_revision': 'bf0701daa9f5b30e5882e2f8f9a5280bcba87e77', 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', - 'spirv_headers_revision': '19e8350415ed9516c8afffa19ae2c58559495a67', + 'spirv_headers_revision': '29817199b7069bac971e5365d180295d4b077ebe', } deps = { -- cgit v1.2.3 From c72c454203ff7e7efb9338b7e65215a125f3f1f2 Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Tue, 9 Nov 2021 10:28:01 -0500 Subject: Clarify how to update DEPS (#4626) - Mention that `depot_tools` are required and link to that repo. - Make `roll_deps.sh` exit on error. The script passes `shellcheck` with this change. - Reword instruction in the README. --- README.md | 10 +++++----- utils/roll_deps.sh | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5cb1b343..14db1e70 100644 --- a/README.md +++ b/README.md @@ -468,11 +468,11 @@ $ANDROID_NDK/ndk-build -C ../android_test \ ### Updating DEPS -Occasionally the entries in [DEPS](DEPS) will need to be updated. This is done on demand -when there is a request to do this, often due to downstream breakages. There is -a script `utils/roll_deps.sh` provided, which will generate a patch with the -updated DEPS values. This will still need to be tested in your checkout to -confirm that there are no integration issues that need to be resolved. +Occasionally the entries in [DEPS](DEPS) will need to be updated. This is done on +demand when there is a request to do this, often due to downstream breakages. +To update `DEPS`, run `utils/roll_deps.sh` and confirm that tests pass. +The script requires Chromium's +[`depot_tools`](https://chromium.googlesource.com/chromium/tools/depot_tools). ## Library diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh index 7ecfdd3b..f61f2a31 100755 --- a/utils/roll_deps.sh +++ b/utils/roll_deps.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2019 Google Inc. +# Copyright (c) 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,10 @@ # Attempts to roll all entries in DEPS to tip-of-tree and create a commit. # -# Depends on roll-dep from depot_path being in PATH. +# Depends on roll-dep from depot_tools +# (https://chromium.googlesource.com/chromium/tools/depot_tools) being in PATH. + +set -eo pipefail effcee_dir="external/effcee/" effcee_trunk="origin/main" @@ -44,3 +47,4 @@ roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}" roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}" git rebase --interactive "${old_head}" + -- cgit v1.2.3 From 352a411278e1934995ed3ad4f55fe854353acca8 Mon Sep 17 00:00:00 2001 From: Greg Fischer Date: Tue, 9 Nov 2021 10:36:50 -0700 Subject: Fix handling of OpPhi in convert-relaxed-to-half (#4618) Fixes #4452 --- source/opt/convert_to_half_pass.cpp | 34 ++++++++----- source/opt/convert_to_half_pass.h | 2 +- test/opt/convert_relaxed_to_half_test.cpp | 81 +++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 14 deletions(-) diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index 0b1afd2a..b127eabe 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp @@ -177,18 +177,21 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) { return modified; } -bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { - // Add float16 converts of any float32 operands and change type - // of phi to float16 equivalent. Operand converts need to be added to - // preceeding blocks. +bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width, + uint32_t to_width) { + // Add converts of any float operands to to_width if they are of from_width. + // If converting to 16, change type of phi to float16 equivalent and remember + // result id. Converts need to be added to preceeding blocks. uint32_t ocnt = 0; uint32_t* prev_idp; - inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) { + bool modified = false; + inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified, + this](uint32_t* idp) { if (ocnt % 2 == 0) { prev_idp = idp; } else { Instruction* val_inst = get_def_use_mgr()->GetDef(*prev_idp); - if (IsFloat(val_inst, 32)) { + if (IsFloat(val_inst, from_width)) { BasicBlock* bp = context()->get_instr_block(*idp); auto insert_before = bp->tail(); if (insert_before != bp->begin()) { @@ -197,15 +200,19 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { insert_before->opcode() != SpvOpLoopMerge) ++insert_before; } - GenConvert(prev_idp, 16, &*insert_before); + GenConvert(prev_idp, to_width, &*insert_before); + modified = true; } } ++ocnt; }); - inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); - get_def_use_mgr()->AnalyzeInstUse(inst); - converted_ids_.insert(inst->result_id()); - return true; + if (to_width == 16u) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u)); + converted_ids_.insert(inst->result_id()); + modified = true; + } + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; } bool ConvertToHalfPass::ProcessConvert(Instruction* inst) { @@ -242,9 +249,10 @@ bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) { } bool ConvertToHalfPass::ProcessDefault(Instruction* inst) { - bool modified = false; // If non-relaxed instruction has changed operands, need to convert // them back to float32 + if (inst->opcode() == SpvOpPhi) return ProcessPhi(inst, 16u, 32u); + bool modified = false; inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { if (converted_ids_.count(*idp) == 0) return; uint32_t old_id = *idp; @@ -262,7 +270,7 @@ bool ConvertToHalfPass::GenHalfInst(Instruction* inst) { if (IsArithmetic(inst) && inst_relaxed) modified = GenHalfArith(inst); else if (inst->opcode() == SpvOpPhi && inst_relaxed) - modified = ProcessPhi(inst); + modified = ProcessPhi(inst, 32u, 16u); else if (inst->opcode() == SpvOpFConvert) modified = ProcessConvert(inst); else if (image_ops_.count(inst->opcode()) != 0) diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h index b647dd4a..c6e84d1b 100644 --- a/source/opt/convert_to_half_pass.h +++ b/source/opt/convert_to_half_pass.h @@ -93,7 +93,7 @@ class ConvertToHalfPass : public Pass { bool GenHalfArith(Instruction* inst); // Gen code for relaxed phi |inst| - bool ProcessPhi(Instruction* inst); + bool ProcessPhi(Instruction* inst, uint32_t from_width, uint32_t to_width); // Gen code for relaxed convert |inst| bool ProcessConvert(Instruction* inst); diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp index 3a798f77..6a06de84 100644 --- a/test/opt/convert_relaxed_to_half_test.cpp +++ b/test/opt/convert_relaxed_to_half_test.cpp @@ -1489,6 +1489,87 @@ TEST_F(ConvertToHalfTest, RemoveRelaxDec) { EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); } +TEST_F(ConvertToHalfTest, HandleNonRelaxedPhi) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4452 + + // This test is a case with a non-relaxed phi with a relaxed operand. + // A convert must be inserted at the end of the block associated with + // the operand. + const std::string test = + R"( +; CHECK: [[fcvt:%\w+]] = OpFConvert %v3float {{%\w+}} +; CHECK-NEXT: OpSelectionMerge {{%\w+}} None +; CHECK: {{%\w+}} = OpPhi %v3float [[fcvt]] {{%\w+}} {{%\w+}} {{%\w+}} + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %output_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %MaterialParams "MaterialParams" + OpMemberName %MaterialParams 0 "foo" + OpName %materialParams "materialParams" + OpName %output_color "output_color" + OpMemberDecorate %MaterialParams 0 Offset 0 + OpDecorate %MaterialParams Block + OpDecorate %materialParams DescriptorSet 0 + OpDecorate %materialParams Binding 5 + OpDecorate %output_color Location 0 + OpDecorate %57 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%MaterialParams = OpTypeStruct %float +%_ptr_Uniform_MaterialParams = OpTypePointer Uniform %MaterialParams +%materialParams = OpVariable %_ptr_Uniform_MaterialParams Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%output_color = OpVariable %_ptr_Output_v4float Output + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %float_0_5 = OpConstant %float 0.5 + %61 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %main = OpFunction %void None %3 + %5 = OpLabel + %55 = OpAccessChain %_ptr_Uniform_float %materialParams %int_0 + %56 = OpLoad %float %55 + %57 = OpCompositeConstruct %v3float %56 %56 %56 + %31 = OpFOrdGreaterThan %bool %56 %float_0 + OpSelectionMerge %33 None + OpBranchConditional %31 %32 %33 + %32 = OpLabel + %37 = OpFMul %v3float %57 %61 + OpBranch %33 + %33 = OpLabel + %58 = OpPhi %v3float %57 %5 %37 %32 + %45 = OpAccessChain %_ptr_Output_float %output_color %uint_0 + %46 = OpCompositeExtract %float %58 0 + OpStore %45 %46 + %48 = OpAccessChain %_ptr_Output_float %output_color %uint_1 + %49 = OpCompositeExtract %float %58 1 + OpStore %48 %49 + %51 = OpAccessChain %_ptr_Output_float %output_color %uint_2 + %52 = OpCompositeExtract %float %58 2 + OpStore %51 %52 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndMatch(test, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From b014238e0836d4e96b28ff7ac3b383025f048f80 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 10 Nov 2021 15:06:39 -0500 Subject: Allow WorkgroupSize on variables for Kernels (#4627) * Update tests --- source/val/validate_annotation.cpp | 3 ++- test/val/val_annotation_test.cpp | 34 ++++++++++++++++++++++++++++++++++ test/val/val_capability_test.cpp | 8 -------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index 16d44906..3a77552e 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -266,7 +266,8 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, << "BuiltIns can only target variables, structure members or " "constants"; } - if (inst->GetOperandAs(2) == SpvBuiltInWorkgroupSize) { + if (_.HasCapability(SpvCapabilityShader) && + inst->GetOperandAs(2) == SpvBuiltInWorkgroupSize) { if (!spvOpcodeIsConstant(target->opcode())) { return fail() << "must be a constant for WorkgroupSize"; } diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp index cd560057..889c76ca 100644 --- a/test/val/val_annotation_test.cpp +++ b/test/val/val_annotation_test.cpp @@ -32,6 +32,40 @@ using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Values; +using DecorationTest = spvtest::ValidateBase; + +TEST_F(DecorationTest, WorkgroupSizeShader) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ones BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%ones = OpConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(DecorationTest, WorkgroupSizeKernel) { + const std::string text = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %var BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%ptr = OpTypePointer Input %int3 +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + using MemberOnlyDecorations = spvtest::ValidateBase; TEST_P(MemberOnlyDecorations, MemberDecoration) { diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index ffd9e0ae..c432c3cf 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -1700,14 +1700,6 @@ std::make_pair(std::string(kOpenCLMemoryModel) + "%ptr = OpTypePointer Input %intt\n" "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), -std::make_pair(std::string(kOpenCLMemoryModel) + - "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %ones BuiltIn WorkgroupSize\n" - "%intt = OpTypeInt 32 0\n" - "%int3 = OpTypeVector %intt 3\n" - "%int_1 = OpConstant %intt 1\n" - "%ones = OpConstantComposite %int3 %int_1 %int_1 %int_1\n" + std::string(kVoidFVoid), - AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %var BuiltIn WorkgroupId\n" -- cgit v1.2.3 From 21e3f681e2004590c7865bc8c0195a4ab8e66c88 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 10 Nov 2021 16:32:09 -0500 Subject: Update SPIRV-Headers (#4628) * Fix compile * Fix test --- DEPS | 2 +- source/opt/reflect.h | 2 +- test/opt/ir_loader_test.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 677244ed..2aaf11f9 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133', 'googletest_revision': 'bf0701daa9f5b30e5882e2f8f9a5280bcba87e77', 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', - 'spirv_headers_revision': '29817199b7069bac971e5365d180295d4b077ebe', + 'spirv_headers_revision': '814e728b30ddd0f4509233099a3ad96fd4318c07', } deps = { diff --git a/source/opt/reflect.h b/source/opt/reflect.h index ffd2805a..c2ffb0be 100644 --- a/source/opt/reflect.h +++ b/source/opt/reflect.h @@ -52,7 +52,7 @@ inline bool IsTypeInst(SpvOp opcode) { } inline bool IsConstantInst(SpvOp opcode) { return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) || - opcode == SpvOpConstFunctionPointerINTEL; + opcode == SpvOpConstantFunctionPointerINTEL; } inline bool IsCompileTimeConstantInst(SpvOp opcode) { return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull; diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp index 6eafcd9d..ccdd032e 100644 --- a/test/opt/ir_loader_test.cpp +++ b/test/opt/ir_loader_test.cpp @@ -115,7 +115,7 @@ TEST(IrBuilder, RoundTripFunctionPointer) { "%float = OpTypeFloat 32\n" "%4 = OpTypeFunction %float %float\n" "%_ptr_Function_4 = OpTypePointer Function %4\n" - "%ptr_to_function = OpConstFunctionPointerINTEL %_ptr_Function_4 " + "%ptr_to_function = OpConstantFunctionPointerINTEL %_ptr_Function_4 " "%some_function\n" "%some_function = OpFunction %float Const %4\n" "%6 = OpFunctionParameter %float\n" -- cgit v1.2.3 From ab8eb607750208066e2d57eff6a34dbaf05f5ada Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Thu, 11 Nov 2021 12:06:08 -0500 Subject: Finalize SPIRV-Tools v2021.4 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4128db1b..ee232b60 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ Revision history for SPIRV-Tools -v2021.4-dev 2021-11-09 +v2021.4 2021-11-11 - General - Add a WebAssembly build (#3752) - Make cxx exceptions controllable (#4591) -- cgit v1.2.3 From ccdf8362077e02e06e7a31add58b450ed8d0926c Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Thu, 11 Nov 2021 12:09:30 -0500 Subject: Start SPIRV-Tools v2021.5 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index ee232b60..4afd14a6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ Revision history for SPIRV-Tools +v2021.5-dev 2021-11-11 + - Start v2021.5-dev + v2021.4 2021-11-11 - General - Add a WebAssembly build (#3752) -- cgit v1.2.3 From 4b092d2ab81854e61632bdd1e658907f0071c37e Mon Sep 17 00:00:00 2001 From: alan-baker Date: Tue, 16 Nov 2021 15:54:17 -0500 Subject: Allow ADCE to remove dead inputs (#4629) * https://github.com/KhronosGroup/Vulkan-Docs/issues/666 clearly specified that interfaces do not require an input if there is an associated output * ADCE can now remove unused input variables (though they are kept if the preserve interfaces option is used) --- source/opt/aggressive_dead_code_elim_pass.cpp | 15 +- test/opt/aggressive_dead_code_elim_test.cpp | 642 +++++--------------------- 2 files changed, 120 insertions(+), 537 deletions(-) diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 0b54d5e8..c2ed2f87 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -569,12 +569,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // Keep all entry points. for (auto& entry : get_module()->entry_points()) { - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && - !preserve_interface_) { - // In SPIR-V 1.4 and later, entry points must list all global variables - // used. DCE can still remove non-input/output variables and update the - // interface list. Mark the entry point as live and inputs and outputs as - // live, but defer decisions all other interfaces. + if (!preserve_interface_) { live_insts_.Set(entry.unique_id()); // The actual function is live always. AddToWorklist( @@ -582,8 +577,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { for (uint32_t i = 3; i < entry.NumInOperands(); ++i) { auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); auto storage_class = var->GetSingleWordInOperand(0u); - if (storage_class == SpvStorageClassInput || - storage_class == SpvStorageClassOutput) { + // Vulkan support outputs without an associated input, but not inputs + // without an associated output. + if (storage_class == SpvStorageClassOutput) { AddToWorklist(var); } } @@ -885,8 +881,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } } - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && - !preserve_interface_) { + if (!preserve_interface_) { // Remove the dead interface variables from the entry point interface list. for (auto& entry : get_module()->entry_points()) { std::vector new_operands; diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index f228c8cb..bc7dd712 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -38,55 +38,45 @@ TEST_F(AggressiveDCETest, EliminateExtendedInst) { // vec4 dv = sqrt(Dead); // gl_FragColor = v; // } - - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" +; CHECK-NOT: OpName %Dead "Dead" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input +; CHECK-NOT: %Dead = OpVariable %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Function_v4float Function %16 = OpLoad %v4float %BaseColor OpStore %v %16 +; CHECK-NOT: OpLoad %v4float %Dead %17 = OpLoad %v4float %Dead +; CHECK-NOT: OpExtInst %v4float %1 Sqrt %18 = OpExtInst %v4float %1 Sqrt %17 +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v OpStore %gl_FragColor %19 @@ -94,21 +84,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%19 = OpLoad %v4float %v -OpStore %gl_FragColor %19 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoEliminateFrexp) { @@ -242,35 +218,23 @@ TEST_F(AggressiveDCETest, EliminateDecorate) { // gl_FragColor = v; // } - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = + R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" OpName %dv "dv" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" +; CHECK-NOT: OpDecorate OpDecorate %8 RelaxedPrecision -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2_before = - R"(%void = OpTypeVoid +%void = OpTypeVoid %10 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 @@ -281,29 +245,14 @@ OpName %gl_FragColor "gl_FragColor" %float_0_5 = OpConstant %float 0.5 %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs2_after = - R"(%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %10 +%main = OpFunction %void None %10 %17 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %dv = OpVariable %_ptr_Function_v4float Function %18 = OpLoad %v4float %BaseColor OpStore %v %18 %19 = OpLoad %v4float %Dead +; CHECK-NOT: OpVectorTimesScalar %8 = OpVectorTimesScalar %v4float %19 %float_0_5 OpStore %dv %8 %20 = OpLoad %v4float %v @@ -312,21 +261,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%17 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%18 = OpLoad %v4float %BaseColor -OpStore %v %18 -%20 = OpLoad %v4float %v -OpStore %gl_FragColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2_before + func_before, - predefs1 + names_after + predefs2_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, Simple) { @@ -342,53 +277,44 @@ TEST_F(AggressiveDCETest, Simple) { // gl_FragColor = v; // } - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = + R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" +; CHECK-NOT: OpName %Dead "Dead" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input +; CHECK-NOT: %Dead = OpVariable %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Function_v4float Function %16 = OpLoad %v4float %BaseColor OpStore %v %16 +; CHECK-NOT: OpLoad %v4float %Dead %17 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %17 %18 = OpLoad %v4float %v OpStore %gl_FragColor %18 @@ -396,21 +322,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%18 = OpLoad %v4float %v -OpStore %gl_FragColor %18 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, OptAllowListExtension) { @@ -426,35 +338,22 @@ TEST_F(AggressiveDCETest, OptAllowListExtension) { // gl_FragColor = v; // } - const std::string predefs1 = + const std::string spirv = R"(OpCapability Shader OpExtension "SPV_AMD_gpu_shader_int16" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" OpName %dv "dv" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 @@ -464,10 +363,7 @@ OpName %gl_FragColor "gl_FragColor" %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %dv = OpVariable %_ptr_Function_v4float Function @@ -481,21 +377,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%18 = OpLoad %v4float %v -OpStore %gl_FragColor %18 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoOptDenyListExtension) { @@ -571,7 +453,7 @@ TEST_F(AggressiveDCETest, ElimWithCall) { // gl_FragColor = vec4(0.0); // } - const std::string defs_before = + const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -600,54 +482,25 @@ OpName %gl_FragColor "gl_FragColor" %gl_FragColor = OpVariable %_ptr_Output_v4float Output %float_0 = OpConstant %float 0 %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 -)"; - - const std::string defs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %i1 %i2 %gl_FragColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 140 -OpName %main "main" -OpName %nothing_vf4_ "nothing(vf4;" -OpName %v "v" -OpName %v1 "v1" -OpName %i1 "i1" -OpName %i2 "i2" -OpName %param "param" -OpName %gl_FragColor "gl_FragColor" -%void = OpTypeVoid -%12 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%16 = OpTypeFunction %void %_ptr_Function_v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%i1 = OpVariable %_ptr_Input_v4float Input -%i2 = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%gl_FragColor = OpVariable %_ptr_Output_v4float Output -%float_0 = OpConstant %float 0 -%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %12 +%main = OpFunction %void None %12 %21 = OpLabel %v1 = OpVariable %_ptr_Function_v4float Function %v2 = OpVariable %_ptr_Function_v4float Function %param = OpVariable %_ptr_Function_v4float Function %22 = OpLoad %v4float %i1 OpStore %v1 %22 +; CHECK-NOT: OpLoad %v4float %i2 %23 = OpLoad %v4float %i2 +; CHECK-NOT: OpStore %v2 OpStore %v2 %23 %24 = OpLoad %v4float %v1 OpStore %param %24 +; CHECK: OpFunctionCall %void %nothing_vf4_ %25 = OpFunctionCall %void %nothing_vf4_ %param OpStore %gl_FragColor %20 OpReturn OpFunctionEnd +; CHECK: %nothing_vf4_ = OpFunction %nothing_vf4_ = OpFunction %void None %16 %v = OpFunctionParameter %_ptr_Function_v4float %26 = OpLabel @@ -655,28 +508,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %12 -%21 = OpLabel -%v1 = OpVariable %_ptr_Function_v4float Function -%param = OpVariable %_ptr_Function_v4float Function -%22 = OpLoad %v4float %i1 -OpStore %v1 %22 -%24 = OpLoad %v4float %v1 -OpStore %param %24 -%25 = OpFunctionCall %void %nothing_vf4_ %param -OpStore %gl_FragColor %20 -OpReturn -OpFunctionEnd -%nothing_vf4_ = OpFunction %void None %16 -%v = OpFunctionParameter %_ptr_Function_v4float -%26 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck(defs_before + func_before, - defs_after + func_after, true, true); + SinglePassRunAndMatch(text, true); } TEST_F(AggressiveDCETest, NoParamElim) { @@ -998,7 +830,7 @@ TEST_F(AggressiveDCETest, PrivateStoreElimInEntryNoCalls) { // OutColor = v; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1008,6 +840,7 @@ OpSource GLSL 450 OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" OpName %Dead "Dead" OpName %OutColor "OutColor" @@ -1019,63 +852,23 @@ OpDecorate %OutColor Location 0 %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Private_v4float = OpTypePointer Private %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%dv = OpVariable %_ptr_Private_v4float Private -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %Dead Location 1 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string main_before = - R"(%main = OpFunction %void None %9 -%16 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%17 = OpLoad %v4float %BaseColor -OpStore %v %17 -%18 = OpLoad %v4float %Dead -OpStore %dv %18 -%19 = OpLoad %v4float %v -%20 = OpFNegate %v4float %19 -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - const std::string main_after = - R"(%main = OpFunction %void None %9 +; CHECK-NOT: OpTypePointer Private +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK-NOT: %dv = OpVariable +%dv = OpVariable %_ptr_Private_v4float Private +%OutColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %9 %16 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %17 = OpLoad %v4float %BaseColor OpStore %v %17 +%18 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv +OpStore %dv %18 %19 = OpLoad %v4float %v %20 = OpFNegate %v4float %19 OpStore %OutColor %20 @@ -1083,8 +876,7 @@ OpReturn OpFunctionEnd )"; - SinglePassRunAndCheck( - predefs_before + main_before, predefs_after + main_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoPrivateStoreElimIfLoad) { @@ -1288,7 +1080,7 @@ TEST_F(AggressiveDCETest, WorkgroupStoreElimInEntryNoCalls) { // OutColor = v; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1298,6 +1090,7 @@ OpSource GLSL 450 OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" OpName %Dead "Dead" OpName %OutColor "OutColor" @@ -1309,49 +1102,22 @@ OpDecorate %OutColor Location 0 %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK-NOT: OpTypePointer Workgroup %_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Workgroup_v4float Workgroup %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %Dead Location 1 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string main_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %16 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %17 = OpLoad %v4float %BaseColor OpStore %v %17 %18 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v %20 = OpFNegate %v4float %19 @@ -1360,21 +1126,7 @@ OpReturn OpFunctionEnd )"; - const std::string main_after = - R"(%main = OpFunction %void None %9 -%16 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%17 = OpLoad %v4float %BaseColor -OpStore %v %17 -%19 = OpLoad %v4float %v -%20 = OpFNegate %v4float %19 -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + main_before, predefs_after + main_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) { @@ -1393,7 +1145,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1424,34 +1176,11 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %7 +; CHECK: = OpFunction %void +; CHECK-NEXT: %22 = OpLabel +; CHECK-NEXT: OpBranch %26 +; CHECK-NEXT: %26 = OpLabel +%main = OpFunction %void None %7 %22 = OpLabel %d = OpVariable %_ptr_Function_float Function %23 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1475,18 +1204,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%22 = OpLabel -OpBranch %26 -%26 = OpLabel -OpStore %OutColor %21 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThen) { @@ -1503,7 +1221,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThen) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1533,34 +1251,11 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %7 +; CHECK: = OpFunction +; CHECK-NEXT: %21 = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +%main = OpFunction %void None %7 %21 = OpLabel %d = OpVariable %_ptr_Function_float Function %22 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1579,18 +1274,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%21 = OpLabel -OpBranch %25 -%25 = OpLabel -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadSwitch) { @@ -1609,7 +1293,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { // } // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1642,6 +1326,10 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel %main = OpFunction %void None %3 %5 = OpLabel %d = OpVariable %_ptr_Function_float Function @@ -1658,45 +1346,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { OpReturn OpFunctionEnd)"; - const std::string after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %x %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %x "x" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %x Flat -OpDecorate %x Location 1 -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%x = OpVariable %_ptr_Input_int Input -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -%main = OpFunction %void None %3 -%5 = OpLabel -OpBranch %11 -%11 = OpLabel -OpStore %OutColor %27 -OpReturn -OpFunctionEnd -)"; - - SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck(before, after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) { @@ -1721,7 +1371,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1754,34 +1404,14 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +; CHECK-NOT: OpLabel - const std::string func_before = - R"(%main = OpFunction %void None %7 +%main = OpFunction %void None %7 %24 = OpLabel %d = OpVariable %_ptr_Function_float Function %25 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1823,18 +1453,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%24 = OpLabel -OpBranch %28 -%28 = OpLabel -OpStore %OutColor %23 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, NoEliminateLiveIfThenElse) { @@ -2578,7 +2197,7 @@ TEST_F(AggressiveDCETest, EliminateEntireFunctionBody) { // d = BaseColor.z; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -2607,32 +2226,15 @@ OpDecorate %OutColor Location 0 %uint_2 = OpConstant %uint 2 %_ptr_Output_v4float = OpTypePointer Output %v4float %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd - const std::string func_before = - R"(%main = OpFunction %void None %7 +%main = OpFunction %void None %7 %20 = OpLabel %d = OpVariable %_ptr_Function_float Function %21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -2655,17 +2257,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%20 = OpLabel -OpBranch %24 -%24 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch(spirv, true); } TEST_F(AggressiveDCETest, EliminateUselessInnerLoop) { @@ -5521,10 +5113,9 @@ OpCapability ImageBuffer OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID +OpEntryPoint GLCompute %2 "min" OpExecutionMode %2 LocalSize 64 1 1 OpSource HLSL 600 -OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId OpDecorate %4 DescriptorSet 4 OpDecorate %4 Binding 70 %uint = OpTypeInt 32 0 @@ -5535,12 +5126,9 @@ OpDecorate %4 Binding 70 %10 = OpTypeFunction %void %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint %_ptr_Image_uint = OpTypePointer Image %uint %4 = OpVariable %_ptr_UniformConstant_6 UniformConstant %16 = OpVariable %_ptr_Private_6 Private -%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input %2 = OpFunction %void None %10 %17 = OpLabel %18 = OpLoad %6 %4 @@ -6393,8 +5981,8 @@ OpFunctionEnd TEST_F(AggressiveDCETest, DeadInputInterfaceV13) { const std::string spirv = R"( -; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] -; CHECK: [[var]] = OpVariable +; CHECK: OpEntryPoint GLCompute %main "main" +; CHECK-NOT: OpVariable OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" %dead @@ -6417,8 +6005,8 @@ OpFunctionEnd TEST_F(AggressiveDCETest, DeadInputInterfaceV14) { const std::string spirv = R"( -; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] -; CHECK: [[var]] = OpVariable +; CHECK: OpEntryPoint GLCompute %main "main" +; CHECK-NOT: OpVariable OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" %dead -- cgit v1.2.3 From 8c155b364ca8585b74c5541c9a238c2f24a0921c Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Wed, 24 Nov 2021 14:13:58 -0500 Subject: Manually fold floating point division by zero (#4637) See https://github.com/KhronosGroup/SPIRV-Tools/issues/4636 for details. Fixes #4636. --- source/opt/amd_ext_to_khr.cpp | 18 +++--- source/opt/const_folding_rules.cpp | 126 ++++++++++++++++++++++++++++++++----- source/opt/constants.cpp | 19 +++++- source/opt/constants.h | 11 +++- source/opt/type_manager.h | 7 +++ test/opt/fold_test.cpp | 76 +++++++++++++++++++++- 6 files changed, 228 insertions(+), 29 deletions(-) diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp index ccedc0bc..d46d2437 100644 --- a/source/opt/amd_ext_to_khr.cpp +++ b/source/opt/amd_ext_to_khr.cpp @@ -584,9 +584,9 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst, } // Get the constants that will be used. - uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); - uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); - uint32_t f0_5_const_id = const_mgr->GetFloatConst(0.5); + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5); const analysis::Constant* vec_const = const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id}); uint32_t vec_const_id = @@ -731,12 +731,12 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst, } // Get the constants that will be used. - uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); - uint32_t f1_const_id = const_mgr->GetFloatConst(1.0); - uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); - uint32_t f3_const_id = const_mgr->GetFloatConst(3.0); - uint32_t f4_const_id = const_mgr->GetFloatConst(4.0); - uint32_t f5_const_id = const_mgr->GetFloatConst(5.0); + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0); + uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0); + uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0); // Extract the input values. Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index 515a3ed5..a9830a25 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp @@ -22,6 +22,45 @@ namespace { const uint32_t kExtractCompositeIdInIdx = 0; +// Returns a constants with the value NaN of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetNan(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits::quiet_NaN()); + case 64: + return const_mgr->GetDoubleConst( + std::numeric_limits::quiet_NaN()); + default: + return nullptr; + } +} + +// Returns a constants with the value INF of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetInf(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits::infinity()); + case 64: + return const_mgr->GetDoubleConst(std::numeric_limits::infinity()); + default: + return nullptr; + } +} + // Returns true if |type| is Float or a vector of Float. bool HasFloatingPoint(const analysis::Type* type) { if (type->AsFloat()) { @@ -33,6 +72,23 @@ bool HasFloatingPoint(const analysis::Type* type) { return false; } +// Returns a constants with the value |-val| of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* negateFPConst(const analysis::Type* result_type, + const analysis::Constant* val, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = result_type->AsFloat(); + assert(float_type != nullptr); + if (float_type->width() == 32) { + float fa = val->GetFloat(); + return const_mgr->GetFloatConst(-fa); + } else if (float_type->width() == 64) { + double da = val->GetDouble(); + return const_mgr->GetDoubleConst(-da); + } + return nullptr; +} + // Folds an OpcompositeExtract where input is a composite constant. ConstantFoldingRule FoldExtractWithConstants() { return [](IRContext* context, Instruction* inst, @@ -492,7 +548,60 @@ ConstantFoldingRule FoldQuantizeToF16() { ConstantFoldingRule FoldFSub() { return FoldFPBinaryOp(FOLD_FPARITH_OP(-)); } ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); } ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); } -ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FOLD_FPARITH_OP(/)); } + +// Returns the constant that results from evaluating |numerator| / 0.0. Returns +// |nullptr| if the result could not be evalutated. +const analysis::Constant* FoldFPScalarDivideByZero( + const analysis::Type* result_type, const analysis::Constant* numerator, + analysis::ConstantManager* const_mgr) { + if (numerator == nullptr) { + return nullptr; + } + + if (numerator->IsZero()) { + return GetNan(result_type, const_mgr); + } + + const analysis::Constant* result = GetInf(result_type, const_mgr); + if (result == nullptr) { + return nullptr; + } + + if (numerator->AsFloatConstant()->GetValueAsDouble() < 0.0) { + result = negateFPConst(result_type, result, const_mgr); + } + return result; +} + +// Returns the result of folding |numerator| / |denominator|. Returns |nullptr| +// if it cannot be folded. +const analysis::Constant* FoldScalarFPDivide( + const analysis::Type* result_type, const analysis::Constant* numerator, + const analysis::Constant* denominator, + analysis::ConstantManager* const_mgr) { + if (denominator == nullptr) { + return nullptr; + } + + if (denominator->IsZero()) { + return FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + } + + const analysis::FloatConstant* denominator_float = + denominator->AsFloatConstant(); + if (denominator_float && denominator->GetValueAsDouble() == -0.0) { + const analysis::Constant* result = + FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + if (result != nullptr) + result = negateFPConst(result_type, result, const_mgr); + return result; + } else { + return FOLD_FPARITH_OP(/)(result_type, numerator, denominator, const_mgr); + } +} + +// Returns the constant folding rule to fold |OpFDiv| with two constants. +ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FoldScalarFPDivide); } bool CompareFloatingPoint(bool op_result, bool op_unordered, bool need_ordered) { @@ -655,20 +764,7 @@ UnaryScalarFoldingRule FoldFNegateOp() { analysis::ConstantManager* const_mgr) -> const analysis::Constant* { assert(result_type != nullptr && a != nullptr); assert(result_type == a->type()); - const analysis::Float* float_type = result_type->AsFloat(); - assert(float_type != nullptr); - if (float_type->width() == 32) { - float fa = a->GetFloat(); - utils::FloatProxy result(-fa); - std::vector words = result.GetWords(); - return const_mgr->GetConstant(result_type, words); - } else if (float_type->width() == 64) { - double da = a->GetDouble(); - utils::FloatProxy result(-da); - std::vector words = result.GetWords(); - return const_mgr->GetConstant(result_type, words); - } - return nullptr; + return negateFPConst(result_type, a, const_mgr); }; } diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index 020e248b..d286cd26 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp @@ -420,13 +420,30 @@ const Constant* ConstantManager::GetNumericVectorConstantWithWords( return GetConstant(type, element_ids); } -uint32_t ConstantManager::GetFloatConst(float val) { +uint32_t ConstantManager::GetFloatConstId(float val) { + const Constant* c = GetFloatConst(val); + return GetDefiningInstruction(c)->result_id(); +} + +const Constant* ConstantManager::GetFloatConst(float val) { Type* float_type = context()->get_type_mgr()->GetFloatType(); utils::FloatProxy v(val); const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + +uint32_t ConstantManager::GetDoubleConstId(double val) { + const Constant* c = GetDoubleConst(val); return GetDefiningInstruction(c)->result_id(); } +const Constant* ConstantManager::GetDoubleConst(double val) { + Type* float_type = context()->get_type_mgr()->GetDoubleType(); + utils::FloatProxy v(val); + const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + uint32_t ConstantManager::GetSIntConst(int32_t val) { Type* sint_type = context()->get_type_mgr()->GetSIntType(); const Constant* c = GetConstant(sint_type, {static_cast(val)}); diff --git a/source/opt/constants.h b/source/opt/constants.h index 52bd809a..10f7bd68 100644 --- a/source/opt/constants.h +++ b/source/opt/constants.h @@ -637,7 +637,16 @@ class ConstantManager { } // Returns the id of a 32-bit floating point constant with value |val|. - uint32_t GetFloatConst(float val); + uint32_t GetFloatConstId(float val); + + // Returns a 32-bit float constant with the given value. + const Constant* GetFloatConst(float val); + + // Returns the id of a 64-bit floating point constant with value |val|. + uint32_t GetDoubleConstId(double val); + + // Returns a 64-bit float constant with the given value. + const Constant* GetDoubleConst(double val); // Returns the id of a 32-bit signed integer constant with value |val|. uint32_t GetSIntConst(int32_t val); diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h index ce9d83d4..72e37f48 100644 --- a/source/opt/type_manager.h +++ b/source/opt/type_manager.h @@ -160,6 +160,13 @@ class TypeManager { uint32_t GetFloatTypeId() { return GetTypeInstruction(GetFloatType()); } + Type* GetDoubleType() { + Float float_type(64); + return GetRegisteredType(&float_type); + } + + uint32_t GetDoubleTypeId() { return GetTypeInstruction(GetDoubleType()); } + Type* GetUIntVectorType(uint32_t size) { Vector vec_type(GetUIntType(), size); return GetRegisteredType(&vec_type); diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 0487e78c..009631c0 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -224,6 +224,7 @@ OpName %main "main" %104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps. %float_null = OpConstantNull %float %float_0 = OpConstant %float 0 +%float_n0 = OpConstant %float -0.0 %float_1 = OpConstant %float 1 %float_2 = OpConstant %float 2 %float_3 = OpConstant %float 3 @@ -249,6 +250,7 @@ OpName %main "main" %105 = OpConstant %double 0 ; Need a def with an numerical id to define id maps. %double_null = OpConstantNull %double %double_0 = OpConstant %double 0 +%double_n0 = OpConstant %double -0.0 %double_1 = OpConstant %double 1 %double_2 = OpConstant %double 2 %double_3 = OpConstant %double 3 @@ -1987,7 +1989,39 @@ INSTANTIATE_TEST_SUITE_P(FloatConstantFoldingTest, FloatInstructionFoldingTest, "%2 = OpExtInst %float %1 Pow %float_2 %float_3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 8.0) + 2, 8.0), + // Test case 43: Fold 1.0 / -0.0. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits::infinity()), + // Test case 44: Fold -1.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_n1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::infinity()), + // Test case 45: Fold 0.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()), + // Test case 46: Fold 0.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_0 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()) )); // clang-format on @@ -2019,7 +2053,11 @@ TEST_P(DoubleInstructionFoldingTest, Case) { const_mrg->GetConstantFromInst(inst)->AsFloatConstant(); EXPECT_NE(result, nullptr); if (result != nullptr) { - EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); + if (!std::isnan(tc.expected_result)) { + EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); + } else { + EXPECT_TRUE(std::isnan(result->GetDoubleValue())); + } } } } @@ -2222,7 +2260,39 @@ INSTANTIATE_TEST_SUITE_P(DoubleConstantFoldingTest, DoubleInstructionFoldingTest "%2 = OpExtInst %double %1 Pow %double_2 %double_3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 8.0) + 2, 8.0), + // Test case 23: Fold 1.0 / -0.0. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_1 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits::infinity()), + // Test case 24: Fold -1.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_n1 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::infinity()), + // Test case 25: Fold 0.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_0 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()), + // Test case 26: Fold 0.0 / -0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_0 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()) )); // clang-format on -- cgit v1.2.3 From b023c0da1d92a99f079e148045758aeed63f801e Mon Sep 17 00:00:00 2001 From: Natalie Chouinard <1953083+sudonatalie@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:58:43 -0500 Subject: Remove default arguments from lambda (#4648) Fixes compile failure due to pedantic warning: `error: default argument specified for lambda parameter [-Werror=pedantic]` --- source/val/validate_annotation.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index 3a77552e..0614e162 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -230,7 +230,7 @@ bool IsNotMemberDecoration(SpvDecoration dec) { spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, const Instruction* inst, const Instruction* target) { - auto fail = [&_, dec, inst, target](uint32_t vuid = 0) -> DiagnosticStream { + auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream { DiagnosticStream ds = std::move( _.diag(SPV_ERROR_INVALID_ID, inst) << _.VkErrorID(vuid) << LogStringForDecoration(dec) @@ -240,7 +240,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, switch (dec) { case SpvDecorationSpecId: if (!spvOpcodeIsScalarSpecConstant(target->opcode())) { - return fail() << "must be a scalar specialization constant"; + return fail(0) << "must be a scalar specialization constant"; } break; case SpvDecorationBlock: @@ -249,14 +249,14 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, case SpvDecorationGLSLPacked: case SpvDecorationCPacked: if (target->opcode() != SpvOpTypeStruct) { - return fail() << "must be a structure type"; + return fail(0) << "must be a structure type"; } break; case SpvDecorationArrayStride: if (target->opcode() != SpvOpTypeArray && target->opcode() != SpvOpTypeRuntimeArray && target->opcode() != SpvOpTypePointer) { - return fail() << "must be an array or pointer type"; + return fail(0) << "must be an array or pointer type"; } break; case SpvDecorationBuiltIn: @@ -269,10 +269,10 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, if (_.HasCapability(SpvCapabilityShader) && inst->GetOperandAs(2) == SpvBuiltInWorkgroupSize) { if (!spvOpcodeIsConstant(target->opcode())) { - return fail() << "must be a constant for WorkgroupSize"; + return fail(0) << "must be a constant for WorkgroupSize"; } } else if (target->opcode() != SpvOpVariable) { - return fail() << "must be a variable"; + return fail(0) << "must be a variable"; } break; case SpvDecorationNoPerspective: @@ -294,10 +294,10 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, case SpvDecorationAliasedPointer: if (target->opcode() != SpvOpVariable && target->opcode() != SpvOpFunctionParameter) { - return fail() << "must be a memory object declaration"; + return fail(0) << "must be a memory object declaration"; } if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) { - return fail() << "must be a pointer type"; + return fail(0) << "must be a pointer type"; } break; case SpvDecorationInvariant: @@ -308,7 +308,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, case SpvDecorationDescriptorSet: case SpvDecorationInputAttachmentIndex: if (target->opcode() != SpvOpVariable) { - return fail() << "must be a variable"; + return fail(0) << "must be a variable"; } break; default: @@ -338,7 +338,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, break; case SpvDecorationIndex: if (sc != SpvStorageClassOutput) { - return fail() << "must be in the Output storage class"; + return fail(0) << "must be in the Output storage class"; } break; case SpvDecorationBinding: @@ -346,13 +346,13 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, if (sc != SpvStorageClassStorageBuffer && sc != SpvStorageClassUniform && sc != SpvStorageClassUniformConstant) { - return fail() << "must be in the StorageBuffer, Uniform, or " - "UniformConstant storage class"; + return fail(0) << "must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class"; } break; case SpvDecorationInputAttachmentIndex: if (sc != SpvStorageClassUniformConstant) { - return fail() << "must be in the UniformConstant storage class"; + return fail(0) << "must be in the UniformConstant storage class"; } break; case SpvDecorationFlat: -- cgit v1.2.3 From d0a827a9f313954935e3d9edb0c063fd8625a22e Mon Sep 17 00:00:00 2001 From: Natalie Chouinard <1953083+sudonatalie@users.noreply.github.com> Date: Mon, 29 Nov 2021 02:11:22 -0500 Subject: Copy OpDecorateStrings in DescriptorScalarReplacementPass (#4649) Along with OpDecorate, also clone the OpDecorateString instructions for variables created in the descriptor scalar replacement pass. Fixes microsoft/DirectXShaderCompiler#3705 --- source/opt/desc_sroa.cpp | 5 +-- source/opt/desc_sroa.h | 9 ++--- test/opt/desc_sroa_test.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index bcbdde94..7263c120 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp @@ -169,7 +169,7 @@ void DescriptorScalarReplacement::CopyDecorationsForNewVariable( Instruction* old_var, uint32_t index, uint32_t new_var_id, uint32_t new_var_ptr_type_id, const bool is_old_var_array, const bool is_old_var_struct, Instruction* old_var_type) { - // Handle OpDecorate instructions. + // Handle OpDecorate and OpDecorateString instructions. for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor(old_var->result_id(), true)) { uint32_t new_binding = 0; @@ -212,7 +212,8 @@ uint32_t DescriptorScalarReplacement::GetNewBindingForElement( void DescriptorScalarReplacement::CreateNewDecorationForNewVariable( Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) { - assert(old_decoration->opcode() == SpvOpDecorate); + assert(old_decoration->opcode() == SpvOpDecorate || + old_decoration->opcode() == SpvOpDecorateString); std::unique_ptr new_decoration(old_decoration->Clone(context())); new_decoration->SetInOperand(0, {new_var_id}); diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index fea06255..383d441c 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h @@ -115,10 +115,11 @@ class DescriptorScalarReplacement : public Pass { const bool is_old_var_struct, Instruction* old_var_type); - // Create a new OpDecorate instruction by cloning |old_decoration|. The new - // OpDecorate instruction will be used for a variable whose id is - // |new_var_ptr_type_id|. If |old_decoration| is a decoration for a binding, - // the new OpDecorate instruction will have |new_binding| as its binding. + // Create a new OpDecorate(String) instruction by cloning |old_decoration|. + // The new OpDecorate(String) instruction will be used for a variable whose id + // is |new_var_ptr_type_id|. If |old_decoration| is a decoration for a + // binding, the new OpDecorate(String) instruction will have |new_binding| as + // its binding. void CreateNewDecorationForNewVariable(Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding); diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp index dcb625d6..91c950e8 100644 --- a/test/opt/desc_sroa_test.cpp +++ b/test/opt/desc_sroa_test.cpp @@ -833,6 +833,93 @@ TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) { SinglePassRunAndMatch(shader, true); } +TEST_F(DescriptorScalarReplacementTest, DecorateStringForReflect) { + // Check that an OpDecorateString instruction is correctly cloned to new + // variable. + + const std::string shader = R"( +; CHECK: OpName %g_testTextures_0_ "g_testTextures[0]" +; CHECK: OpDecorate %g_testTextures_0_ DescriptorSet 0 +; CHECK: OpDecorate %g_testTextures_0_ Binding 0 +; CHECK: OpDecorateString %g_testTextures_0_ UserTypeGOOGLE "texture2d" + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_testTextures "g_testTextures" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %param_var_vPixelPos "param.var.vPixelPos" + OpName %src_main "src.main" + OpName %vPixelPos "vPixelPos" + OpName %bb_entry "bb.entry" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorateString %out_var_SV_Target UserSemantic "SV_Target" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_testTextures DescriptorSet 0 + OpDecorate %g_testTextures Binding 0 + OpDecorateString %g_testTextures UserTypeGOOGLE "texture2d" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 +%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %25 = OpTypeFunction %v4float %_ptr_Function_v4float + %v2float = OpTypeVector %float 2 + %v3uint = OpTypeVector %uint 3 + %v3int = OpTypeVector %int 3 + %v2int = OpTypeVector %int 2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%g_testTextures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %18 + %19 = OpLabel +%param_var_vPixelPos = OpVariable %_ptr_Function_v4float Function + %22 = OpLoad %v4float %gl_FragCoord + OpStore %param_var_vPixelPos %22 + %23 = OpFunctionCall %v4float %src_main %param_var_vPixelPos + OpStore %out_var_SV_Target %23 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %25 + %vPixelPos = OpFunctionParameter %_ptr_Function_v4float + %bb_entry = OpLabel + %28 = OpLoad %v4float %vPixelPos + %30 = OpVectorShuffle %v2float %28 %28 0 1 + %31 = OpCompositeExtract %float %30 0 + %32 = OpCompositeExtract %float %30 1 + %33 = OpConvertFToU %uint %31 + %34 = OpConvertFToU %uint %32 + %36 = OpCompositeConstruct %v3uint %33 %34 %uint_0 + %38 = OpBitcast %v3int %36 + %40 = OpVectorShuffle %v2int %38 %38 0 1 + %41 = OpCompositeExtract %int %38 2 + %43 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_testTextures %int_0 + %44 = OpLoad %type_2d_image %43 + %45 = OpImageFetch %v4float %44 %40 Lod %41 + OpReturnValue %45 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(shader, true); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From c75a1a46f39f93ecbd4bf86a0cb0ff84b387b6ba Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Thu, 2 Dec 2021 10:40:28 -0500 Subject: Fix https://github.com/KhronosGroup/SPIRV-Tools/issues/4462 (#4651) This prevents CCP from making constant -> constant transitions when evaluating instruction values. In this case, FClamp is evaluated twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING) which returns 0.5. Both fold() computations are correct given the semantics of FClamp() but this causes a lateral transition in the constant lattice which was not being considered VARYING by CCP. --- source/opt/ccp_pass.cpp | 43 ++++++++++++++++++++++---- source/opt/ccp_pass.h | 16 ++++++++++ test/opt/ccp_test.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp index 8b896d50..5099b477 100644 --- a/source/opt/ccp_pass.cpp +++ b/source/opt/ccp_pass.cpp @@ -102,6 +102,34 @@ SSAPropagator::PropStatus CCPPass::VisitPhi(Instruction* phi) { return SSAPropagator::kInteresting; } +uint32_t CCPPass::ComputeLatticeMeet(Instruction* instr, uint32_t val2) { + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + auto val1_it = values_.find(instr->result_id()); + if (val1_it == values_.end()) { + return val2; + } + + uint32_t val1 = val1_it->second; + if (IsVaryingValue(val1)) { + return val1; + } else if (IsVaryingValue(val2)) { + return val2; + } else if (val1 != val2) { + return kVaryingSSAId; + } + return val2; +} + SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { assert(instr->result_id() != 0 && "Expecting an instruction that produces a result"); @@ -115,8 +143,10 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { if (IsVaryingValue(it->second)) { return MarkInstructionVarying(instr); } else { - values_[instr->result_id()] = it->second; - return SSAPropagator::kInteresting; + uint32_t new_val = ComputeLatticeMeet(instr, it->second); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; } } return SSAPropagator::kNotInteresting; @@ -142,9 +172,12 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { if (folded_inst != nullptr) { // We do not want to change the body of the function by adding new // instructions. When folding we can only generate new constants. - assert(folded_inst->IsConstant() && "CCP is only interested in constant."); - values_[instr->result_id()] = folded_inst->result_id(); - return SSAPropagator::kInteresting; + assert(folded_inst->IsConstant() && + "CCP is only interested in constant values."); + uint32_t new_val = ComputeLatticeMeet(instr, folded_inst->result_id()); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; } // Conservatively mark this instruction as varying if any input id is varying. diff --git a/source/opt/ccp_pass.h b/source/opt/ccp_pass.h index fb20c780..77ea9f80 100644 --- a/source/opt/ccp_pass.h +++ b/source/opt/ccp_pass.h @@ -92,6 +92,22 @@ class CCPPass : public MemPass { // generated during propagation. analysis::ConstantManager* const_mgr_; + // Returns a new value for |instr| by computing the meet operation between + // its existing value and |val2|. + // + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + uint32_t ComputeLatticeMeet(Instruction* instr, uint32_t val2); + // Constant value table. Each entry in this map // represents the compile-time constant value for |id| as declared by // |const_decl_id|. Each |const_decl_id| in this table is an OpConstant diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp index 212e0514..ae7043b9 100644 --- a/test/opt/ccp_test.cpp +++ b/test/opt/ccp_test.cpp @@ -1234,6 +1234,86 @@ OpFunctionEnd SinglePassRunAndCheck(text, text, false); } + +// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/4462. +// The test was causing a lateral movement in the constant lattice, which was +// not being detected as varying by CCP. In this test, FClamp is evaluated +// twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which +// returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING) +// which returns 0.5. +// +// Both fold() computations are correct given the semantics of FClamp() but +// this causes a lateral transition in the constant lattice which was not being +// considered VARYING by CCP. +TEST_F(CCPTest, LateralLatticeTransition) { + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %outColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %outColor "outColor" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %outColor Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %float_n1 = OpConstant %float -1 + %float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %outColor = OpVariable %_ptr_Output_v4float Output + +; This constant is created during the first evaluation of the CompositeConstruct +; CHECK: [[new_constant:%\d+]] = OpConstantComposite %v4float %float_n1 %float_0_5 %float_0 %float_1 + + %main = OpFunction %void None %6 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %21 = OpLoad %float %20 + %22 = OpFOrdLessThan %bool %21 %float_0 + OpSelectionMerge %23 None + OpBranchConditional %22 %24 %25 + %24 = OpLabel + OpBranch %23 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpBranch %23 + %23 = OpLabel + %27 = OpPhi %float %float_n1 %24 %float_0_5 %26 + %28 = OpExtInst %float %1 FClamp %float_0_5 %float_0_5 %27 + + ; On first evaluation, the result from FClamp will return 0.5. + ; But on second evaluation, FClamp should return VARYING. Check + ; that CCP is not keeping the first result. + ; CHECK-NOT: %29 = OpCompositeConstruct %v4float %float_0_5 %float_0_5 %float_0 %float_1 + %29 = OpCompositeConstruct %v4float %28 %float_0_5 %float_0 %float_1 + + ; CHECK-NOT: OpCopyObject %v4float [[new_constant]] + %42 = OpCopyObject %v4float %29 + + ; CHECK-NOT: OpStore %outColor [[new_constant]] + OpStore %outColor %42 + + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndMatch(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From f9bcc82ec730fd040325405a318919331a0be798 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Sat, 4 Dec 2021 07:18:21 +0000 Subject: Exit when ID overflow occurs in a fuzzing build (#4652) Currently if an ID overflow occurs, spirv-opt (and other users of IRContext) emits a warning and starts returning 0 when fresh ids are requested. This tends to lead to crashes - such as null pointer exceptions. When these arise during fuzzing they lead to auto-reported bugs. This change uses an ifdef guard to instead gracefully exit as soon as an ID overflow occurs when the build is a fuzzing build. Related issue: #4539. --- source/opt/ir_context.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 65853476..2ce16db4 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -518,6 +518,18 @@ class IRContext { std::string message = "ID overflow. Try running compact-ids."; consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // If TakeNextId returns 0, it is very likely that execution will + // subsequently fail. Such failures are false alarms from a fuzzing point + // of view: they are due to the fact that too many ids were used, rather + // than being due to an actual bug. Thus, during a fuzzing build, it is + // preferable to bail out when ID overflow occurs. + // + // A zero exit code is returned here because a non-zero code would cause + // ClusterFuzz/OSS-Fuzz to regard the termination as a crash, and spurious + // crash reports is what this guard aims to avoid. + exit(0); +#endif } return next_id; } -- cgit v1.2.3 From 1f3f0f185b627625dedde62c68672bf12d548ba4 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Wed, 8 Dec 2021 16:09:25 +0000 Subject: Avoid uninitialised read when parsing hex float (#4646) Ensures that when an attempt to read a floating-point value from an input stream fails, the recieving variable for the read does not end up uninitialised. Fixes OSS-Fuzz:40432 Fixes #4644 --- source/util/hex_float.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/util/hex_float.h b/source/util/hex_float.h index be28eae3..903b6288 100644 --- a/source/util/hex_float.h +++ b/source/util/hex_float.h @@ -199,7 +199,7 @@ bool operator==(const FloatProxy& first, const FloatProxy& second) { // Reads a FloatProxy value as a normal float from a stream. template std::istream& operator>>(std::istream& is, FloatProxy& value) { - T float_val; + T float_val = static_cast(0.0); is >> float_val; value = FloatProxy(float_val); return is; -- cgit v1.2.3 From b162ede0dea5bb3a4f802a6c29a5be26b5caf097 Mon Sep 17 00:00:00 2001 From: Shahbaz Youssefi Date: Wed, 8 Dec 2021 11:55:36 -0500 Subject: Use schema instead of reserved in header description (#4615) For consistency with SPIR-V disassembly which outputs Schema for this field. --- source/link/linker.cpp | 2 +- source/opt/module.cpp | 2 +- source/opt/module.h | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source/link/linker.cpp b/source/link/linker.cpp index c5ca5625..c4bfc763 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -210,7 +210,7 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer, header->version = version; header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0); header->bound = max_id_bound; - header->reserved = 0u; + header->schema = 0u; return SPV_SUCCESS; } diff --git a/source/opt/module.cpp b/source/opt/module.cpp index f97defbd..c3c70598 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -139,7 +139,7 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { // TODO(antiagainst): should we change the generator number? binary->push_back(header_.generator); binary->push_back(header_.bound); - binary->push_back(header_.reserved); + binary->push_back(header_.schema); size_t bound_idx = binary->size() - 2; DebugScope last_scope(kNoDebugScope, kNoInlinedAt); diff --git a/source/opt/module.h b/source/opt/module.h index 0360b7d5..230be709 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -36,7 +36,7 @@ struct ModuleHeader { uint32_t version; uint32_t generator; uint32_t bound; - uint32_t reserved; + uint32_t schema; }; // A SPIR-V module. It contains all the information for a SPIR-V module and @@ -61,7 +61,7 @@ class Module { } // Returns the Id bound. - uint32_t IdBound() { return header_.bound; } + uint32_t IdBound() const { return header_.bound; } // Returns the current Id bound and increases it to the next available value. // If the id bound has already reached its maximum value, then 0 is returned. @@ -141,6 +141,8 @@ class Module { inline uint32_t id_bound() const { return header_.bound; } inline uint32_t version() const { return header_.version; } + inline uint32_t generator() const { return header_.generator; } + inline uint32_t schema() const { return header_.schema; } inline void set_version(uint32_t v) { header_.version = v; } -- cgit v1.2.3 From 1ed847f43804efaac09e67ebdd86783d801f7b62 Mon Sep 17 00:00:00 2001 From: Marius Hillenbrand Date: Wed, 8 Dec 2021 18:01:26 +0100 Subject: Fix endianness of string literals (#4622) * Fix endianness of string literals To get correct and consistent encoding and decoding of string literals on big-endian platforms, use spvtools::utils::MakeString and MakeVector (or wrapper functions) consistently for handling string literals. - add variant of MakeVector that encodes a string literal into an existing vector of words - add variants of MakeString - add a wrapper spvDecodeLiteralStringOperand in source/ - fix wrapper Operand::AsString to use MakeString (source/opt) - remove Operand::AsCString as broken and unused - add a variant of GetOperandAs for string literals (source/val) ... and apply those wrappers throughout the code. Fixes #149 * Extend round trip test for StringLiterals to flip word order In the encoding/decoding roundtrip tests for string literals, include a case that flips byte order in words after encoding and then checks for successful decoding. That is, on a little-endian host flip to big-endian byte order and then decode, and vice versa. * BinaryParseTest.InstructionWithStringOperand: also flip byte order Test binary parsing of string operands both with the host's and with the reversed byte order. --- source/binary.cpp | 35 +++++++------- source/binary.h | 7 +++ source/disassemble.cpp | 12 ++--- source/extensions.cpp | 4 +- source/link/linker.cpp | 20 ++++---- source/name_mapper.cpp | 11 ++--- source/opt/aggressive_dead_code_elim_pass.cpp | 12 ++--- source/opt/amd_ext_to_khr.cpp | 6 +-- source/opt/feature_manager.cpp | 3 +- source/opt/graphics_robust_access_pass.cpp | 10 ++-- source/opt/inst_debug_printf_pass.cpp | 12 ++--- source/opt/instruction.h | 11 ++--- source/opt/ir_context.cpp | 11 ++--- source/opt/ir_context.h | 13 ++---- source/opt/local_access_chain_convert_pass.cpp | 12 ++--- source/opt/local_single_block_elim_pass.cpp | 12 ++--- source/opt/local_single_store_elim_pass.cpp | 12 ++--- source/opt/module.cpp | 4 +- source/opt/remove_duplicates_pass.cpp | 5 +- source/opt/replace_invalid_opc.cpp | 5 +- source/opt/strip_debug_info_pass.cpp | 13 +++--- source/opt/strip_reflect_info_pass.cpp | 15 +++--- source/opt/type_manager.cpp | 9 ++-- source/opt/upgrade_memory_model.cpp | 8 ++-- source/text_handler.cpp | 11 ++--- source/util/string_utils.h | 63 +++++++++++++++++++++----- source/val/instruction.cpp | 10 ++++ source/val/instruction.h | 3 ++ source/val/validate.cpp | 9 ++-- source/val/validate_decorations.cpp | 6 ++- source/val/validate_extensions.cpp | 11 ++--- source/val/validation_state.cpp | 6 +-- test/binary_parse_test.cpp | 53 +++++++++------------- test/binary_to_text.literal_test.cpp | 17 +++++-- test/opt/instruction_test.cpp | 6 --- test/test_fixture.h | 19 +++++++- 36 files changed, 249 insertions(+), 227 deletions(-) diff --git a/source/binary.cpp b/source/binary.cpp index 93d5da7a..48a94f1e 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -33,6 +33,7 @@ #include "source/operand.h" #include "source/spirv_constant.h" #include "source/spirv_endian.h" +#include "source/util/string_utils.h" spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, const spv_endianness_t endian, @@ -62,6 +63,15 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, return SPV_SUCCESS; } +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { + assert(operand_index < inst.num_operands); + const spv_parsed_operand_t& operand = inst.operands[operand_index]; + + return spvtools::utils::MakeString(inst.words + operand.offset, + operand.num_words); +} + namespace { // A SPIR-V binary parser. A parser instance communicates detailed parse @@ -577,27 +587,18 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_LITERAL_STRING: case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { - convert_operand_endianness = false; - const char* string = - reinterpret_cast(_.words + _.word_index); - // Compute the length of the string, but make sure we don't run off the - // end of the input. - const size_t remaining_input_bytes = - sizeof(uint32_t) * (_.num_words - _.word_index); - const size_t string_num_content_bytes = - spv_strnlen_s(string, remaining_input_bytes); - // If there was no terminating null byte, then that's an end-of-input - // error. - if (string_num_content_bytes == remaining_input_bytes) + const size_t max_words = _.num_words - _.word_index; + std::string string = + spvtools::utils::MakeString(_.words + _.word_index, max_words, false); + + if (string.length() == max_words * 4) return exhaustedInputDiagnostic(inst_offset, opcode, type); - // Account for null in the word length, so add 1 for null, then add 3 to - // make sure we round up. The following is equivalent to: - // (string_num_content_bytes + 1 + 3) / 4 - const size_t string_num_words = string_num_content_bytes / 4 + 1; + // Make sure we can record the word count without overflow. // // This error can't currently be triggered because of validity // checks elsewhere. + const size_t string_num_words = string.length() / 4 + 1; if (string_num_words > std::numeric_limits::max()) { return diagnostic() << "Literal string is longer than " << std::numeric_limits::max() @@ -611,7 +612,7 @@ spv_result_t Parser::parseOperand(size_t inst_offset, // There is only one string literal argument to OpExtInstImport, // so it's sufficient to guard this just on the opcode. const spv_ext_inst_type_t ext_inst_type = - spvExtInstImportTypeGet(string); + spvExtInstImportTypeGet(string.c_str()); if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { return diagnostic() << "Invalid extended instruction import '" << string << "'"; diff --git a/source/binary.h b/source/binary.h index 66d24c7e..eb3beaca 100644 --- a/source/binary.h +++ b/source/binary.h @@ -15,6 +15,8 @@ #ifndef SOURCE_BINARY_H_ #define SOURCE_BINARY_H_ +#include + #include "source/spirv_definition.h" #include "spirv-tools/libspirv.h" @@ -33,4 +35,9 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, // replacement for C11's strnlen_s which might not exist in all environments. size_t spv_strnlen_s(const char* str, size_t strsz); +// Decode the string literal operand with index operand_index from instruction +// inst. +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + #endif // SOURCE_BINARY_H_ diff --git a/source/disassemble.cpp b/source/disassemble.cpp index c553988f..250c2bf9 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -283,13 +283,11 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, case SPV_OPERAND_TYPE_LITERAL_STRING: { stream_ << "\""; SetGreen(); - // Strings are always little-endian, and null-terminated. - // Write out the characters, escaping as needed, and without copying - // the entire string. - auto c_str = reinterpret_cast(inst.words + operand.offset); - for (auto p = c_str; *p; ++p) { - if (*p == '"' || *p == '\\') stream_ << '\\'; - stream_ << *p; + + std::string str = spvDecodeLiteralStringOperand(inst, operand_index); + for (char const& c : str) { + if (c == '"' || c == '\\') stream_ << '\\'; + stream_ << c; } ResetColor(); stream_ << '"'; diff --git a/source/extensions.cpp b/source/extensions.cpp index a94db273..049a3ad1 100644 --- a/source/extensions.cpp +++ b/source/extensions.cpp @@ -18,6 +18,7 @@ #include #include +#include "source/binary.h" #include "source/enum_string_mapping.h" namespace spvtools { @@ -30,8 +31,9 @@ std::string GetExtensionString(const spv_parsed_instruction_t* inst) { const auto& operand = inst->operands[0]; assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING); assert(inst->num_words > operand.offset); + (void)operand; /* No unused variables in release builds. */ - return reinterpret_cast(inst->words + operand.offset); + return spvDecodeLiteralStringOperand(*inst, 0); } std::string ExtensionSetToString(const ExtensionSet& extensions) { diff --git a/source/link/linker.cpp b/source/link/linker.cpp index c4bfc763..21cada70 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -37,6 +37,7 @@ #include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" #include "spirv-tools/libspirv.hpp" namespace spvtools { @@ -282,16 +283,15 @@ spv_result_t MergeModules(const MessageConsumer& consumer, memory_model_inst->Clone(linked_context))); } while (false); - std::vector> entry_points; + std::vector> entry_points; for (const auto& module : input_modules) for (const auto& inst : module->entry_points()) { const uint32_t model = inst.GetSingleWordInOperand(0); - const char* const name = - reinterpret_cast(inst.GetInOperand(2).words.data()); + const std::string name = inst.GetInOperand(2).AsString(); const auto i = std::find_if( entry_points.begin(), entry_points.end(), - [model, name](const std::pair& v) { - return v.first == model && strcmp(name, v.second) == 0; + [model, name](const std::pair& v) { + return v.first == model && v.second == name; }); if (i != entry_points.end()) { spv_operand_desc desc = nullptr; @@ -334,11 +334,8 @@ spv_result_t MergeModules(const MessageConsumer& consumer, // OpModuleProcessed instruction about the linking step. if (linked_module->version() >= 0x10100) { const std::string processed_string("Linked by SPIR-V Tools Linker"); - const auto num_chars = processed_string.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector processed_words(num_words, 0u); - std::memcpy(processed_words.data(), processed_string.data(), num_chars); + std::vector processed_words = + spvtools::utils::MakeVector(processed_string); linked_module->AddDebug3Inst(std::unique_ptr( new Instruction(linked_context, SpvOpModuleProcessed, 0u, 0u, {{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}}))); @@ -414,8 +411,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer, const uint32_t type = decoration.GetSingleWordInOperand(3u); LinkageSymbolInfo symbol_info; - symbol_info.name = - reinterpret_cast(decoration.GetInOperand(2u).words.data()); + symbol_info.name = decoration.GetInOperand(2u).AsString(); symbol_info.id = id; symbol_info.type_id = 0u; diff --git a/source/name_mapper.cpp b/source/name_mapper.cpp index eb08f8fe..3b31d33a 100644 --- a/source/name_mapper.cpp +++ b/source/name_mapper.cpp @@ -22,10 +22,10 @@ #include #include -#include "spirv-tools/libspirv.h" - +#include "source/binary.h" #include "source/latest_version_spirv_header.h" #include "source/parsed_operand.h" +#include "spirv-tools/libspirv.h" namespace spvtools { namespace { @@ -172,7 +172,7 @@ spv_result_t FriendlyNameMapper::ParseInstruction( const auto result_id = inst.result_id; switch (inst.opcode) { case SpvOpName: - SaveName(inst.words[1], reinterpret_cast(inst.words + 2)); + SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1)); break; case SpvOpDecorate: // Decorations come after OpName. So OpName will take precedence over @@ -274,9 +274,8 @@ spv_result_t FriendlyNameMapper::ParseInstruction( SaveName(result_id, "Queue"); break; case SpvOpTypeOpaque: - SaveName(result_id, - std::string("Opaque_") + - Sanitize(reinterpret_cast(inst.words + 2))); + SaveName(result_id, std::string("Opaque_") + + Sanitize(spvDecodeLiteralStringOperand(inst, 1))); break; case SpvOpTypePipeStorage: SaveName(result_id, "PipeStorage"); diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index c2ed2f87..9827c535 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -27,6 +27,7 @@ #include "source/opt/iterator.h" #include "source/opt/reflect.h" #include "source/spirv_constant.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -146,8 +147,7 @@ void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { bool AggressiveDCEPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -156,11 +156,9 @@ bool AggressiveDCEPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp index d46d2437..dd9bafda 100644 --- a/source/opt/amd_ext_to_khr.cpp +++ b/source/opt/amd_ext_to_khr.cpp @@ -935,8 +935,7 @@ Pass::Status AmdExtensionToKhrPass::Process() { std::vector to_be_killed; for (Instruction& inst : context()->module()->extensions()) { if (inst.opcode() == SpvOpExtension) { - if (ext_to_remove.count(reinterpret_cast( - &(inst.GetInOperand(0).words[0]))) != 0) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { to_be_killed.push_back(&inst); } } @@ -944,8 +943,7 @@ Pass::Status AmdExtensionToKhrPass::Process() { for (Instruction& inst : context()->ext_inst_imports()) { if (inst.opcode() == SpvOpExtInstImport) { - if (ext_to_remove.count(reinterpret_cast( - &(inst.GetInOperand(0).words[0]))) != 0) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { to_be_killed.push_back(&inst); } } diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index 39a4a348..a5902716 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -39,8 +39,7 @@ void FeatureManager::AddExtension(Instruction* ext) { assert(ext->opcode() == SpvOpExtension && "Expecting an extension instruction."); - const std::string name = - reinterpret_cast(ext->GetInOperand(0u).words.data()); + const std::string name = ext->GetInOperand(0u).AsString(); Extension extension; if (GetExtensionFromString(name.c_str(), &extension)) { extensions_.Add(extension); diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index 1b28f9b5..336dcd83 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp @@ -559,21 +559,17 @@ uint32_t GraphicsRobustAccessPass::GetGlslInsts() { if (module_status_.glsl_insts_id == 0) { // This string serves double-duty as raw data for a string and for a vector // of 32-bit words - const char glsl[] = "GLSL.std.450\0\0\0\0"; - const size_t glsl_str_byte_len = 16; + const char glsl[] = "GLSL.std.450"; // Use an existing import if we can. for (auto& inst : context()->module()->ext_inst_imports()) { - const auto& name_words = inst.GetInOperand(0).words; - if (0 == std::strncmp(reinterpret_cast(name_words.data()), - glsl, glsl_str_byte_len)) { + if (inst.GetInOperand(0).AsString() == glsl) { module_status_.glsl_insts_id = inst.result_id(); } } if (module_status_.glsl_insts_id == 0) { // Make a new import instruction. module_status_.glsl_insts_id = TakeNextId(); - std::vector words(glsl_str_byte_len / sizeof(uint32_t)); - std::memcpy(words.data(), glsl, glsl_str_byte_len); + std::vector words = spvtools::utils::MakeVector(glsl); auto import_inst = MakeUnique( context(), SpvOpExtInstImport, 0, module_status_.glsl_insts_id, std::initializer_list{ diff --git a/source/opt/inst_debug_printf_pass.cpp b/source/opt/inst_debug_printf_pass.cpp index c0e6bc3f..4218138f 100644 --- a/source/opt/inst_debug_printf_pass.cpp +++ b/source/opt/inst_debug_printf_pass.cpp @@ -16,6 +16,7 @@ #include "inst_debug_printf_pass.h" +#include "source/util/string_utils.h" #include "spirv/unified1/NonSemanticDebugPrintf.h" namespace spvtools { @@ -231,10 +232,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() { 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))) { + const std::string set_name = c_itr->GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(set_name, "NonSemantic.")) { non_sem_set_seen = true; break; } @@ -242,9 +241,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() { 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")) { + const std::string ext_name = c_itr->GetInOperand(0).AsString(); + if (ext_name == "SPV_KHR_non_semantic_info") { context()->KillInst(&*c_itr); break; } diff --git a/source/opt/instruction.h b/source/opt/instruction.h index ce568f66..57ee7073 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -24,6 +24,7 @@ #include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/binary.h" #include "source/common_debug_info.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_spirv_header.h" @@ -32,6 +33,7 @@ #include "source/opt/reflect.h" #include "source/util/ilist_node.h" #include "source/util/small_vector.h" +#include "source/util/string_utils.h" #include "spirv-tools/libspirv.h" const uint32_t kNoDebugScope = 0; @@ -85,15 +87,12 @@ 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 { + // Returns a string operand as a std::string. + std::string AsString() const { assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); - return reinterpret_cast(words.data()); + return spvtools::utils::MakeString(words); } - // Returns a string operand as a std::string. - std::string AsString() const { return AsCString(); } - // Returns a literal integer operand as a uint64_t uint64_t AsLiteralUint64() const { assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER); diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 612a831a..fef0f7ce 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -623,9 +623,8 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) { void IRContext::AddCombinatorsForExtension(Instruction* extension) { assert(extension->opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast(&extension->GetInOperand(0).words[0]); - if (!strcmp(extension_name, "GLSL.std.450")) { + const std::string extension_name = extension->GetInOperand(0).AsString(); + if (extension_name == "GLSL.std.450") { combinator_ops_[extension->result_id()] = {GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, @@ -944,11 +943,11 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { uint32_t line_number = 0; uint32_t col_number = 0; - char* source = nullptr; + std::string source; if (line_inst != nullptr) { Instruction* file_name = get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0)); - source = reinterpret_cast(&file_name->GetInOperand(0).words[0]); + source = file_name->GetInOperand(0).AsString(); // Get the line number and column number. line_number = line_inst->GetSingleWordInOperand(1); @@ -957,7 +956,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { message += "\n " + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - consumer()(SPV_MSG_ERROR, source, {line_number, col_number, 0}, + consumer()(SPV_MSG_ERROR, source.c_str(), {line_number, col_number, 0}, message.c_str()); } diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 2ce16db4..7bef3054 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -43,6 +43,7 @@ #include "source/opt/type_manager.h" #include "source/opt/value_number_table.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -1032,11 +1033,7 @@ void IRContext::AddCapability(std::unique_ptr&& c) { } void IRContext::AddExtension(const std::string& ext_name) { - const auto num_chars = ext_name.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector ext_words(num_words, 0u); - std::memcpy(ext_words.data(), ext_name.data(), num_chars); + std::vector ext_words = spvtools::utils::MakeVector(ext_name); AddExtension(std::unique_ptr( new Instruction(this, SpvOpExtension, 0u, 0u, {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); @@ -1053,11 +1050,7 @@ void IRContext::AddExtension(std::unique_ptr&& e) { } void IRContext::AddExtInstImport(const std::string& name) { - const auto num_chars = name.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector ext_words(num_words, 0u); - std::memcpy(ext_words.data(), name.data(), num_chars); + std::vector ext_words = spvtools::utils::MakeVector(name); AddExtInstImport(std::unique_ptr( new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(), {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index da9ba8cc..d2059f5c 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -19,6 +19,7 @@ #include "ir_builder.h" #include "ir_context.h" #include "iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -328,8 +329,7 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const { return false; // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -339,11 +339,9 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index 5fd4f658..f48c56aa 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -19,6 +19,7 @@ #include #include "source/opt/iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -183,8 +184,7 @@ void LocalSingleBlockLoadStoreElimPass::Initialize() { bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -194,11 +194,9 @@ bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index 051bcada..123d03bf 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -19,6 +19,7 @@ #include "source/cfa.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -48,8 +49,7 @@ bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) { bool LocalSingleStoreElimPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -59,11 +59,9 @@ bool LocalSingleStoreElimPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } diff --git a/source/opt/module.cpp b/source/opt/module.cpp index c3c70598..5983abb1 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -260,9 +260,7 @@ bool Module::HasExplicitCapability(uint32_t cap) { uint32_t Module::GetExtInstImportId(const char* extstr) { for (auto& ei : ext_inst_imports_) - if (!strcmp(extstr, - reinterpret_cast(&(ei.GetInOperand(0).words[0])))) - return ei.result_id(); + if (!ei.GetInOperand(0).AsString().compare(extstr)) return ei.result_id(); return 0; } diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp index 0e65cc8d..1ed8e2a0 100644 --- a/source/opt/remove_duplicates_pass.cpp +++ b/source/opt/remove_duplicates_pass.cpp @@ -72,9 +72,8 @@ bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const { std::unordered_map ext_inst_imports; for (auto* i = &*context()->ext_inst_import_begin(); i;) { - auto res = ext_inst_imports.emplace( - reinterpret_cast(i->GetInOperand(0u).words.data()), - i->result_id()); + auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(), + i->result_id()); if (res.second) { // Never seen before, keep it. i = i->NextNode(); diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp index e3b9d3e4..1dcd06f5 100644 --- a/source/opt/replace_invalid_opc.cpp +++ b/source/opt/replace_invalid_opc.cpp @@ -112,8 +112,7 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, } Instruction* file_name = context()->get_def_use_mgr()->GetDef(file_name_id); - const char* source = reinterpret_cast( - &file_name->GetInOperand(0).words[0]); + const std::string source = file_name->GetInOperand(0).AsString(); // Get the line number and column number. uint32_t line_number = @@ -121,7 +120,7 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, uint32_t col_number = last_line_dbg_inst->GetSingleWordInOperand(2); // Replace the instruction. - ReplaceInstruction(inst, source, line_number, col_number); + ReplaceInstruction(inst, source.c_str(), line_number, col_number); } } }, diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp index c86ce578..6a0ebf24 100644 --- a/source/opt/strip_debug_info_pass.cpp +++ b/source/opt/strip_debug_info_pass.cpp @@ -14,6 +14,7 @@ #include "source/opt/strip_debug_info_pass.h" #include "source/opt/ir_context.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -21,9 +22,8 @@ namespace opt { Pass::Status StripDebugInfoPass::Process() { bool uses_non_semantic_info = false; for (auto& inst : context()->module()->extensions()) { - const char* ext_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_KHR_non_semantic_info") { uses_non_semantic_info = true; } } @@ -46,9 +46,10 @@ Pass::Status StripDebugInfoPass::Process() { if (use->opcode() == SpvOpExtInst) { auto ext_inst_set = def_use->GetDef(use->GetSingleWordInOperand(0u)); - const char* extension_name = reinterpret_cast( - &ext_inst_set->GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + const std::string extension_name = + ext_inst_set->GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, + "NonSemantic.")) { // found a non-semantic use, return false as we cannot // remove this OpString return false; diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp index 8b0f2db7..f9be960a 100644 --- a/source/opt/strip_reflect_info_pass.cpp +++ b/source/opt/strip_reflect_info_pass.cpp @@ -19,6 +19,7 @@ #include "source/opt/instruction.h" #include "source/opt/ir_context.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -60,14 +61,13 @@ Pass::Status StripReflectInfoPass::Process() { } for (auto& inst : context()->module()->extensions()) { - const char* ext_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strcmp(ext_name, "SPV_GOOGLE_hlsl_functionality1")) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { to_remove.push_back(&inst); } else if (!other_uses_for_decorate_string && - 0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) { + ext_name == "SPV_GOOGLE_decorate_string") { to_remove.push_back(&inst); - } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + } else if (ext_name == "SPV_KHR_non_semantic_info") { to_remove.push_back(&inst); } } @@ -84,9 +84,8 @@ Pass::Status StripReflectInfoPass::Process() { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.")) { non_semantic_sets.insert(inst.result_id()); to_remove.push_back(&inst); } diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp index 7935ad33..6da4b57b 100644 --- a/source/opt/type_manager.cpp +++ b/source/opt/type_manager.cpp @@ -23,6 +23,7 @@ #include "source/opt/log.h" #include "source/opt/reflect.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -349,11 +350,8 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { } case Type::kOpaque: { const Opaque* opaque = type->AsOpaque(); - size_t size = opaque->name().size(); // Convert to null-terminated packed UTF-8 string. - std::vector words(size / 4 + 1, 0); - char* dst = reinterpret_cast(words.data()); - strncpy(dst, opaque->name().c_str(), size); + std::vector words = spvtools::utils::MakeVector(opaque->name()); typeInst = MakeUnique( context(), SpvOpTypeOpaque, 0, id, std::initializer_list{ @@ -781,8 +779,7 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) { } } break; case SpvOpTypeOpaque: { - const uint32_t* data = inst.GetInOperand(0).words.data(); - type = new Opaque(reinterpret_cast(data)); + type = new Opaque(inst.GetInOperand(0).AsString()); } break; case SpvOpTypePointer: { uint32_t pointee_type_id = inst.GetSingleWordInOperand(1); diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp index ab252059..9d6a5bce 100644 --- a/source/opt/upgrade_memory_model.cpp +++ b/source/opt/upgrade_memory_model.cpp @@ -20,6 +20,7 @@ #include "source/opt/ir_context.h" #include "source/spirv_constant.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -58,9 +59,7 @@ void UpgradeMemoryModel::UpgradeMemoryModelInstruction() { std::initializer_list{ {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityVulkanMemoryModelKHR}}})); const std::string extension = "SPV_KHR_vulkan_memory_model"; - std::vector words(extension.size() / 4 + 1, 0); - char* dst = reinterpret_cast(words.data()); - strncpy(dst, extension.c_str(), extension.size()); + std::vector words = spvtools::utils::MakeVector(extension); context()->AddExtension( MakeUnique(context(), SpvOpExtension, 0, 0, std::initializer_list{ @@ -85,8 +84,7 @@ void UpgradeMemoryModel::UpgradeInstructions() { if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) { auto import = get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); - if (reinterpret_cast(import->GetInOperand(0u).words.data()) == - std::string("GLSL.std.450")) { + if (import->GetInOperand(0u).AsString() == "GLSL.std.450") { UpgradeExtInst(inst); } } diff --git a/source/text_handler.cpp b/source/text_handler.cpp index 46b98456..fe12a26e 100644 --- a/source/text_handler.cpp +++ b/source/text_handler.cpp @@ -29,6 +29,7 @@ #include "source/util/bitutils.h" #include "source/util/hex_float.h" #include "source/util/parse_number.h" +#include "source/util/string_utils.h" namespace spvtools { namespace { @@ -307,14 +308,8 @@ spv_result_t AssemblyContext::binaryEncodeString(const char* value, << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words."; } - pInst->words.resize(newWordCount); - - // Make sure all the bytes in the last word are 0, in case we only - // write a partial word at the end. - pInst->words.back() = 0; - - char* dest = (char*)&pInst->words[oldWordCount]; - strncpy(dest, value, length + 1); + pInst->words.reserve(newWordCount); + spvtools::utils::AppendToVector(value, &pInst->words); return SPV_SUCCESS; } diff --git a/source/util/string_utils.h b/source/util/string_utils.h index 4282aa94..03e20b3d 100644 --- a/source/util/string_utils.h +++ b/source/util/string_utils.h @@ -16,6 +16,8 @@ #define SOURCE_UTIL_STRING_UTILS_H_ #include + +#include #include #include #include @@ -44,9 +46,10 @@ std::string CardinalToOrdinal(size_t cardinal); // string will be empty. std::pair SplitFlagArgs(const std::string& flag); -// Encodes a string as a sequence of words, using the SPIR-V encoding. -inline std::vector MakeVector(std::string input) { - std::vector result; +// Encodes a string as a sequence of words, using the SPIR-V encoding, appending +// to an existing vector. +inline void AppendToVector(const std::string& input, + std::vector* result) { uint32_t word = 0; size_t num_bytes = input.size(); // SPIR-V strings are null-terminated. The byte_index == num_bytes @@ -56,24 +59,36 @@ inline std::vector MakeVector(std::string input) { (byte_index < num_bytes ? uint8_t(input[byte_index]) : uint8_t(0)); word |= (new_byte << (8 * (byte_index % sizeof(uint32_t)))); if (3 == (byte_index % sizeof(uint32_t))) { - result.push_back(word); + result->push_back(word); word = 0; } } // Emit a trailing partial word. if ((num_bytes + 1) % sizeof(uint32_t)) { - result.push_back(word); + result->push_back(word); } +} + +// Encodes a string as a sequence of words, using the SPIR-V encoding. +inline std::vector MakeVector(const std::string& input) { + std::vector result; + AppendToVector(input, &result); return result; } -// Decode a string from a sequence of words, using the SPIR-V encoding. -template -inline std::string MakeString(const VectorType& words) { +// Decode a string from a sequence of words between first and last, using the +// SPIR-V encoding. Assert that a terminating 0-byte was found (unless +// assert_found_terminating_null is passed as false). +template +inline std::string MakeString(InputIt first, InputIt last, + bool assert_found_terminating_null = true) { std::string result; + constexpr size_t kCharsPerWord = sizeof(*first); + static_assert(kCharsPerWord == 4, "expect 4-byte word"); - for (uint32_t word : words) { - for (int byte_index = 0; byte_index < 4; byte_index++) { + for (InputIt pos = first; pos != last; ++pos) { + uint32_t word = *pos; + for (size_t byte_index = 0; byte_index < kCharsPerWord; byte_index++) { uint32_t extracted_word = (word >> (8 * byte_index)) & 0xFF; char c = static_cast(extracted_word); if (c == 0) { @@ -82,9 +97,33 @@ inline std::string MakeString(const VectorType& words) { result += c; } } - assert(false && "Did not find terminating null for the string."); + assert(!assert_found_terminating_null && + "Did not find terminating null for the string."); + (void)assert_found_terminating_null; /* No unused parameters in release + builds. */ return result; -} // namespace utils +} + +// Decode a string from a sequence of words in a vector, using the SPIR-V +// encoding. +template +inline std::string MakeString(const VectorType& words, + bool assert_found_terminating_null = true) { + return MakeString(words.cbegin(), words.cend(), + assert_found_terminating_null); +} + +// Decode a string from array words, consuming up to count words, using the +// SPIR-V encoding. +inline std::string MakeString(const uint32_t* words, size_t num_words, + bool assert_found_terminating_null = true) { + return MakeString(words, words + num_words, assert_found_terminating_null); +} + +// Check if str starts with prefix (only included since C++20) +inline bool starts_with(const std::string& str, const char* prefix) { + return 0 == str.compare(0, std::strlen(prefix), prefix); +} } // namespace utils } // namespace spvtools diff --git a/source/val/instruction.cpp b/source/val/instruction.cpp index b9155898..f16fcd73 100644 --- a/source/val/instruction.cpp +++ b/source/val/instruction.cpp @@ -16,6 +16,9 @@ #include +#include "source/binary.h" +#include "source/util/string_utils.h" + namespace spvtools { namespace val { @@ -41,5 +44,12 @@ bool operator==(const Instruction& lhs, uint32_t rhs) { return lhs.id() == rhs; } +template <> +std::string Instruction::GetOperandAs(size_t index) const { + const spv_parsed_operand_t& o = operands_.at(index); + assert(o.offset + o.num_words <= inst_.num_words); + return spvtools::utils::MakeString(words_.data() + o.offset, o.num_words); +} + } // namespace val } // namespace spvtools diff --git a/source/val/instruction.h b/source/val/instruction.h index 617cb066..6d1f9f4f 100644 --- a/source/val/instruction.h +++ b/source/val/instruction.h @@ -133,6 +133,9 @@ bool operator<(const Instruction& lhs, uint32_t rhs); bool operator==(const Instruction& lhs, const Instruction& rhs); bool operator==(const Instruction& lhs, uint32_t rhs); +template <> +std::string Instruction::GetOperandAs(size_t index) const; + } // namespace val } // namespace spvtools diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 45b6a463..7655c960 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -219,9 +219,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( if (inst->opcode() == SpvOpEntryPoint) { const auto entry_point = inst->GetOperandAs(1); const auto execution_model = inst->GetOperandAs(0); - const char* str = reinterpret_cast( - inst->words().data() + inst->operand(2).offset); - const std::string desc_name(str); + const std::string desc_name = inst->GetOperandAs(2); ValidationState_t::EntryPointDescription desc; desc.name = desc_name; @@ -237,9 +235,8 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( for (const Instruction* check_inst : visited_entry_points) { const auto check_execution_model = check_inst->GetOperandAs(0); - const char* check_str = reinterpret_cast( - check_inst->words().data() + inst->operand(2).offset); - const std::string check_name(check_str); + const std::string check_name = + check_inst->GetOperandAs(2); if (desc_name == check_name && execution_model == check_execution_model) { diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 3cdb471c..50c0db93 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -21,11 +21,13 @@ #include #include +#include "source/binary.h" #include "source/diagnostic.h" #include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/spirv_validator_options.h" +#include "source/util/string_utils.h" #include "source/val/validate_scopes.h" #include "source/val/validation_state.h" @@ -798,8 +800,8 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { // targeted by an OpEntryPoint instruction for (auto& decoration : vstate.id_decorations(entry_point)) { if (SpvDecorationLinkageAttributes == decoration.dec_type()) { - const char* linkage_name = - reinterpret_cast(&decoration.params()[0]); + const std::string linkage_name = + spvtools::utils::MakeString(decoration.params()); return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "The LinkageAttributes Decoration (Linkage name: " diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index dccbe149..479e9e41 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -280,8 +280,7 @@ spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; } - const std::string name_str = reinterpret_cast( - name->words().data() + name->operands()[1].offset); + const std::string name_str = name->GetOperandAs(1); bool found = false; for (auto& desc : _.entry_point_descriptions(kernel_id)) { if (name_str == desc.name) { @@ -741,8 +740,7 @@ spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { const auto name_id = 1; if (!_.HasExtension(kSPV_KHR_non_semantic_info)) { - const std::string name(reinterpret_cast( - inst->words().data() + inst->operands()[name_id].offset)); + const std::string name = inst->GetOperandAs(name_id); if (name.find("NonSemantic.") == 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "NonSemantic extended instruction sets cannot be declared " @@ -774,7 +772,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { assert(import_inst); std::ostringstream ss; - ss << reinterpret_cast(import_inst->words().data() + 2); + ss << import_inst->GetOperandAs(1); ss << " "; ss << desc->name; @@ -3264,8 +3262,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { } } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { auto import_inst = _.FindDef(inst->GetOperandAs(2)); - const std::string name(reinterpret_cast( - import_inst->words().data() + import_inst->operands()[1].offset)); + const std::string name = import_inst->GetOperandAs(1); const std::string reflection = "NonSemantic.ClspvReflection."; char* end_ptr; auto version_string = name.substr(reflection.size()); diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 8d1a0d3f..9d708be4 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -497,15 +497,13 @@ void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { switch (inst->opcode()) { case SpvOpName: { const auto target = inst->GetOperandAs(0); - const auto* str = reinterpret_cast(inst->words().data() + - inst->operand(1).offset); + const std::string str = inst->GetOperandAs(1); AssignNameToId(target, str); break; } case SpvOpMemberName: { const auto target = inst->GetOperandAs(0); - const auto* str = reinterpret_cast(inst->words().data() + - inst->operand(2).offset); + const std::string str = inst->GetOperandAs(2); AssignNameToId(target, str); break; } diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp index 9a13f22c..ece750c5 100644 --- a/test/binary_parse_test.cpp +++ b/test/binary_parse_test.cpp @@ -203,16 +203,7 @@ class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> { void Parse(const SpirvVector& words, spv_result_t expected_result, bool flip_words = false) { SpirvVector flipped_words(words); - SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness"); - if (flip_words) { - std::transform(flipped_words.begin(), flipped_words.end(), - flipped_words.begin(), [](const uint32_t raw_word) { - return spvFixWord(raw_word, - I32_ENDIAN_HOST == I32_ENDIAN_BIG - ? SPV_ENDIANNESS_LITTLE - : SPV_ENDIANNESS_BIG); - }); - } + MaybeFlipWords(flip_words, flipped_words.begin(), flipped_words.end()); EXPECT_EQ(expected_result, spvBinaryParse(ScopedContext().context, &client_, flipped_words.data(), flipped_words.size(), @@ -486,27 +477,27 @@ TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) { } TEST_F(BinaryParseTest, InstructionWithStringOperand) { - const std::string str = - "the future is already here, it's just not evenly distributed"; - const auto str_words = MakeVector(str); - const auto instruction = MakeInstruction(SpvOpName, {99}, str_words); - const auto words = Concatenate({ExpectedHeaderForBound(100), instruction}); - InSequence calls_expected_in_specific_order; - EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS)); - const auto operands = std::vector{ - MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID), - MakeLiteralStringOperand(2, static_cast(str_words.size()))}; - EXPECT_CALL(client_, - Instruction(ParsedInstruction(spv_parsed_instruction_t{ - instruction.data(), static_cast(instruction.size()), - SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/, - 0 /* No result id for OpName*/, operands.data(), - static_cast(operands.size())}))) - .WillOnce(Return(SPV_SUCCESS)); - // Since we are actually checking the output, don't test the - // endian-swapped version. - Parse(words, SPV_SUCCESS, false); - EXPECT_EQ(nullptr, diagnostic_); + for (bool endian_swap : kSwapEndians) { + const std::string str = + "the future is already here, it's just not evenly distributed"; + const auto str_words = MakeVector(str); + const auto instruction = MakeInstruction(SpvOpName, {99}, str_words); + const auto words = Concatenate({ExpectedHeaderForBound(100), instruction}); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS)); + const auto operands = std::vector{ + MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID), + MakeLiteralStringOperand(2, static_cast(str_words.size()))}; + EXPECT_CALL(client_, Instruction(ParsedInstruction(spv_parsed_instruction_t{ + instruction.data(), + static_cast(instruction.size()), + SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/, + 0 /* No result id for OpName*/, operands.data(), + static_cast(operands.size())}))) + .WillOnce(Return(SPV_SUCCESS)); + Parse(words, SPV_SUCCESS, endian_swap); + EXPECT_EQ(nullptr, diagnostic_); + } } // Checks for non-zero values for the result_id and ext_inst_type members diff --git a/test/binary_to_text.literal_test.cpp b/test/binary_to_text.literal_test.cpp index 02daac76..5956984b 100644 --- a/test/binary_to_text.literal_test.cpp +++ b/test/binary_to_text.literal_test.cpp @@ -27,8 +27,15 @@ using ::testing::Eq; using RoundTripLiteralsTest = spvtest::TextToBinaryTestBase<::testing::TestWithParam>; +static const bool kSwapEndians[] = {false, true}; + TEST_P(RoundTripLiteralsTest, Sample) { - EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam())); + for (bool endian_swap : kSwapEndians) { + EXPECT_THAT( + EncodeAndDecodeSuccessfully(GetParam(), SPV_BINARY_TO_TEXT_OPTION_NONE, + SPV_ENV_UNIVERSAL_1_0, endian_swap), + Eq(GetParam())); + } } // clang-format off @@ -58,8 +65,12 @@ using RoundTripSpecialCaseLiteralsTest = spvtest::TextToBinaryTestBase< // Test case where the generated disassembly is not the same as the // assembly passed in. TEST_P(RoundTripSpecialCaseLiteralsTest, Sample) { - EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam())), - Eq(std::get<1>(GetParam()))); + for (bool endian_swap : kSwapEndians) { + EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam()), + SPV_BINARY_TO_TEXT_OPTION_NONE, + SPV_ENV_UNIVERSAL_1_0, endian_swap), + Eq(std::get<1>(GetParam()))); + } } // clang-format off diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp index c5b92efb..2a48134d 100644 --- a/test/opt/instruction_test.cpp +++ b/test/opt/instruction_test.cpp @@ -62,12 +62,6 @@ 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)); diff --git a/test/test_fixture.h b/test/test_fixture.h index 0c5bfc9c..029fc854 100644 --- a/test/test_fixture.h +++ b/test/test_fixture.h @@ -15,6 +15,7 @@ #ifndef TEST_TEST_FIXTURE_H_ #define TEST_TEST_FIXTURE_H_ +#include #include #include @@ -91,12 +92,26 @@ class TextToBinaryTestBase : public T { return diagnostic->error; } + // Potentially flip the words in the binary representation to the other + // endianness + template + void MaybeFlipWords(bool flip_words, It begin, It end) { + SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness"); + if (flip_words) { + std::transform(begin, end, begin, [](const uint32_t raw_word) { + return spvFixWord(raw_word, I32_ENDIAN_HOST == I32_ENDIAN_BIG + ? SPV_ENDIANNESS_LITTLE + : SPV_ENDIANNESS_BIG); + }); + } + } + // Encodes SPIR-V text into binary and then decodes the binary using // given options. Returns the decoded text. std::string EncodeAndDecodeSuccessfully( const std::string& txt, uint32_t disassemble_options = SPV_BINARY_TO_TEXT_OPTION_NONE, - spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + spv_target_env env = SPV_ENV_UNIVERSAL_1_0, bool flip_words = false) { DestroyBinary(); DestroyDiagnostic(); ScopedContext context(env); @@ -110,6 +125,8 @@ class TextToBinaryTestBase : public T { EXPECT_EQ(SPV_SUCCESS, error); if (!binary) return ""; + MaybeFlipWords(flip_words, binary->code, binary->code + binary->wordCount); + spv_text decoded_text; error = spvBinaryToText(context.context, binary->code, binary->wordCount, disassemble_options, &decoded_text, &diagnostic); -- cgit v1.2.3 From 64328e94dbd1512b342546377980f08ec4068044 Mon Sep 17 00:00:00 2001 From: Sebastien Alaiwan Date: Wed, 8 Dec 2021 20:53:36 +0100 Subject: Avoid an extra map lookup (#4623) Speedup: 5% less time on a real-world compilation batch (nearly 10k parallel compilations) (Durations: Before: 156.5s, After: 147.4s) --- source/opt/def_use_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp index 394b9fa1..772483f2 100644 --- a/source/opt/def_use_manager.cpp +++ b/source/opt/def_use_manager.cpp @@ -260,7 +260,7 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { id_to_users_.erase( UserEntry(GetDef(use_id), const_cast(inst))); } - inst_to_used_ids_.erase(inst); + inst_to_used_ids_.erase(iter); } } -- cgit v1.2.3 From 438096e0c2524d8733aea6e9d56dc47921eedc79 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Wed, 8 Dec 2021 15:26:29 -0500 Subject: Fix kokoro asan run (#4655) With a change in the VM, the kokoro asan run is failing because it does not have the correct permissions. Adding the ptrace capability will hopefully fix that. --- kokoro/scripts/linux/build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh index 4731ebdc..85d4b61a 100644 --- a/kokoro/scripts/linux/build.sh +++ b/kokoro/scripts/linux/build.sh @@ -26,7 +26,9 @@ COMPILER=$2 TOOL=$3 BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +# "--privileged" is required to run ptrace in the asan builds. docker run --rm -i \ + --privileged \ --volume "${ROOT_DIR}:${ROOT_DIR}" \ --volume "${KOKORO_ARTIFACTS_DIR}:${KOKORO_ARTIFACTS_DIR}" \ --workdir "${ROOT_DIR}" \ -- cgit v1.2.3 From f37551d2b617cf8b057e4dee55c9fa0d4d672be3 Mon Sep 17 00:00:00 2001 From: Sebastien Alaiwan Date: Thu, 9 Dec 2021 15:40:29 +0100 Subject: Use a struct (instead of tuple), with explicit member names. (#4621) * Cleanup includes. * Simplify assertion. * Use a struct with named members for 'UserEntry' --- source/opt/def_use_manager.cpp | 19 +++++++------------ source/opt/def_use_manager.h | 36 ++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp index 772483f2..824956f1 100644 --- a/source/opt/def_use_manager.cpp +++ b/source/opt/def_use_manager.cpp @@ -14,11 +14,6 @@ #include "source/opt/def_use_manager.h" -#include - -#include "source/opt/log.h" -#include "source/opt/reflect.h" - namespace spvtools { namespace opt { namespace analysis { @@ -58,8 +53,8 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { case SPV_OPERAND_TYPE_SCOPE_ID: { uint32_t use_id = inst->GetSingleWordOperand(i); Instruction* def = GetDef(use_id); - if (!def) assert(false && "Definition is not registered."); - id_to_users_.insert(UserEntry(def, inst)); + assert(def && "Definition is not registered."); + id_to_users_.insert(UserEntry{def, inst}); used_ids->push_back(use_id); } break; default: @@ -102,13 +97,13 @@ const Instruction* DefUseManager::GetDef(uint32_t id) const { DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin( const Instruction* def) const { return id_to_users_.lower_bound( - UserEntry(const_cast(def), nullptr)); + UserEntry{const_cast(def), nullptr}); } bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, const IdToUsersMap::const_iterator& cached_end, const Instruction* inst) const { - return (iter != cached_end && iter->first == inst); + return (iter != cached_end && iter->def == inst); } bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, @@ -125,7 +120,7 @@ bool DefUseManager::WhileEachUser( auto end = id_to_users_.end(); for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { - if (!f(iter->second)) return false; + if (!f(iter->user)) return false; } return true; } @@ -158,7 +153,7 @@ bool DefUseManager::WhileEachUse( auto end = id_to_users_.end(); for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { - Instruction* user = iter->second; + Instruction* user = iter->user; for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) { const Operand& op = user->GetOperand(idx); if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) { @@ -258,7 +253,7 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { if (iter != inst_to_used_ids_.end()) { for (auto use_id : iter->second) { id_to_users_.erase( - UserEntry(GetDef(use_id), const_cast(inst))); + UserEntry{GetDef(use_id), const_cast(inst)}); } inst_to_used_ids_.erase(iter); } diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h index 0499e82b..9d31de53 100644 --- a/source/opt/def_use_manager.h +++ b/source/opt/def_use_manager.h @@ -15,10 +15,8 @@ #ifndef SOURCE_OPT_DEF_USE_MANAGER_H_ #define SOURCE_OPT_DEF_USE_MANAGER_H_ -#include #include #include -#include #include #include "source/opt/instruction.h" @@ -51,15 +49,17 @@ inline bool operator<(const Use& lhs, const Use& rhs) { return lhs.operand_index < rhs.operand_index; } -// Definition and user pair. -// -// The first element of the pair is the definition. -// The second element of the pair is the user. -// // Definition should never be null. User can be null, however, such an entry // should be used only for searching (e.g. all users of a particular definition) // and never stored in a container. -using UserEntry = std::pair; +struct UserEntry { + Instruction* def; + Instruction* user; +}; + +inline bool operator==(const UserEntry& lhs, const UserEntry& rhs) { + return lhs.def == rhs.def && lhs.user == rhs.user; +} // Orders UserEntry for use in associative containers (i.e. less than ordering). // @@ -72,24 +72,24 @@ using UserEntry = std::pair; // definition (i.e. using {def, nullptr}). struct UserEntryLess { bool operator()(const UserEntry& lhs, const UserEntry& rhs) const { - // If lhs.first and rhs.first are both null, fall through to checking the + // If lhs.def and rhs.def are both null, fall through to checking the // second entries. - if (!lhs.first && rhs.first) return true; - if (lhs.first && !rhs.first) return false; + if (!lhs.def && rhs.def) return true; + if (lhs.def && !rhs.def) return false; // If neither definition is null, then compare unique ids. - if (lhs.first && rhs.first) { - if (lhs.first->unique_id() < rhs.first->unique_id()) return true; - if (rhs.first->unique_id() < lhs.first->unique_id()) return false; + if (lhs.def && rhs.def) { + if (lhs.def->unique_id() < rhs.def->unique_id()) return true; + if (rhs.def->unique_id() < lhs.def->unique_id()) return false; } // Return false on equality. - if (!lhs.second && !rhs.second) return false; - if (!lhs.second) return true; - if (!rhs.second) return false; + if (!lhs.user && !rhs.user) return false; + if (!lhs.user) return true; + if (!rhs.user) return false; // If neither user is null then compare unique ids. - return lhs.second->unique_id() < rhs.second->unique_id(); + return lhs.user->unique_id() < rhs.user->unique_id(); } }; -- cgit v1.2.3 From b9e255b3663c29686ef91e0d332c1ba82930bbb5 Mon Sep 17 00:00:00 2001 From: Sebastien Alaiwan Date: Thu, 9 Dec 2021 15:41:42 +0100 Subject: DefUseManager: rename comparison operators to 'CompareAndPrintDifferences' (#4624) This make sense, as those are actually debug functions and shouldn't be used in production code. --- source/opt/def_use_manager.cpp | 26 +++++++++++++++----------- source/opt/def_use_manager.h | 6 ++---- source/opt/ir_context.cpp | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp index 824956f1..d54fdb65 100644 --- a/source/opt/def_use_manager.cpp +++ b/source/opt/def_use_manager.cpp @@ -259,49 +259,53 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { } } -bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) { +bool CompareAndPrintDifferences(const DefUseManager& lhs, + const DefUseManager& rhs) { + bool same = true; + if (lhs.id_to_def_ != rhs.id_to_def_) { for (auto p : lhs.id_to_def_) { if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) { - return false; + printf("Diff in id_to_def: missing value in rhs\n"); } } for (auto p : rhs.id_to_def_) { if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) { - return false; + printf("Diff in id_to_def: missing value in lhs\n"); } } - return false; + same = false; } if (lhs.id_to_users_ != rhs.id_to_users_) { for (auto p : lhs.id_to_users_) { if (rhs.id_to_users_.count(p) == 0) { - return false; + printf("Diff in id_to_users: missing value in rhs\n"); } } for (auto p : rhs.id_to_users_) { if (lhs.id_to_users_.count(p) == 0) { - return false; + printf("Diff in id_to_users: missing value in lhs\n"); } } - return false; + same = false; } if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) { for (auto p : lhs.inst_to_used_ids_) { if (rhs.inst_to_used_ids_.count(p.first) == 0) { - return false; + printf("Diff in inst_to_used_ids: missing value in rhs\n"); } } for (auto p : rhs.inst_to_used_ids_) { if (lhs.inst_to_used_ids_.count(p.first) == 0) { - return false; + printf("Diff in inst_to_used_ids: missing value in lhs\n"); } } - return false; + same = false; } - return true; + + return same; } } // namespace analysis diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h index 9d31de53..c4901f8f 100644 --- a/source/opt/def_use_manager.h +++ b/source/opt/def_use_manager.h @@ -210,10 +210,8 @@ class DefUseManager { // Erases the records that a given instruction uses its operand ids. void EraseUseRecordsOfOperandIds(const Instruction* inst); - friend bool operator==(const DefUseManager&, const DefUseManager&); - friend bool operator!=(const DefUseManager& lhs, const DefUseManager& rhs) { - return !(lhs == rhs); - } + friend bool CompareAndPrintDifferences(const DefUseManager&, + const DefUseManager&); // If |inst| has not already been analysed, then analyses its defintion and // uses. diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index fef0f7ce..78918461 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -317,7 +317,7 @@ bool IRContext::IsConsistent() { #else if (AreAnalysesValid(kAnalysisDefUse)) { analysis::DefUseManager new_def_use(module()); - if (*get_def_use_mgr() != new_def_use) { + if (!CompareAndPrintDifferences(*get_def_use_mgr(), new_def_use)) { return false; } } -- cgit v1.2.3 From 6926c3d9a39429114c121dd4b70a5d546b661ad4 Mon Sep 17 00:00:00 2001 From: Andrei Malashkin Date: Thu, 9 Dec 2021 15:42:40 +0100 Subject: treat google user type as normal semantic google. It's a backport from diligent fork (#4632) --- source/opt/strip_reflect_info_pass.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp index f9be960a..cf74fa00 100644 --- a/source/opt/strip_reflect_info_pass.cpp +++ b/source/opt/strip_reflect_info_pass.cpp @@ -33,7 +33,8 @@ Pass::Status StripReflectInfoPass::Process() { for (auto& inst : context()->module()->annotations()) { switch (inst.opcode()) { case SpvOpDecorateStringGOOGLE: - if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE) { + if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(1) == SpvDecorationUserTypeGOOGLE) { to_remove.push_back(&inst); } else { other_uses_for_decorate_string = true; @@ -41,7 +42,8 @@ Pass::Status StripReflectInfoPass::Process() { break; case SpvOpMemberDecorateStringGOOGLE: - if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE) { + if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(2) == SpvDecorationUserTypeGOOGLE) { to_remove.push_back(&inst); } else { other_uses_for_decorate_string = true; @@ -64,6 +66,8 @@ Pass::Status StripReflectInfoPass::Process() { const std::string ext_name = inst.GetInOperand(0).AsString(); if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { to_remove.push_back(&inst); + } else if (0 == std::strcmp(ext_name, "SPV_GOOGLE_user_type")) { + to_remove.push_back(&inst); } else if (!other_uses_for_decorate_string && ext_name == "SPV_GOOGLE_decorate_string") { to_remove.push_back(&inst); -- cgit v1.2.3 From d240d0db92d5fe7013fcbc0f66925632c7088aa4 Mon Sep 17 00:00:00 2001 From: "jiaxin.lai" Date: Thu, 9 Dec 2021 22:43:55 +0800 Subject: fix file encoding (#4654) --- source/val/validate_image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 64f6ba7b..59000a2d 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Google Inc. +// Copyright (c) 2017 Google Inc. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights // reserved. // -- cgit v1.2.3 From a2260d3b1fd2405a8625d4a21792fb84157c7eaf Mon Sep 17 00:00:00 2001 From: Sebastien Alaiwan Date: Thu, 9 Dec 2021 18:42:53 +0100 Subject: Fix compilation (#4656) * Delete public accessor, whose only user is one unit-test The underlying container type becomes invisible from the outside. * Fix compilation --- source/opt/def_use_manager.h | 4 +--- source/opt/strip_reflect_info_pass.cpp | 2 +- test/opt/def_use_test.cpp | 7 ++++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h index c4901f8f..d66575d3 100644 --- a/source/opt/def_use_manager.h +++ b/source/opt/def_use_manager.h @@ -97,7 +97,6 @@ struct UserEntryLess { class DefUseManager { public: using IdToDefMap = std::unordered_map; - using IdToUsersMap = std::set; // Constructs a def-use manager from the given |module|. All internal messages // will be communicated to the outside via the given message |consumer|. This @@ -197,8 +196,6 @@ class DefUseManager { // Returns the map from ids to their def instructions. const IdToDefMap& id_to_defs() const { return id_to_def_; } - // Returns the map from instructions to their users. - const IdToUsersMap& id_to_users() const { return id_to_users_; } // Clear the internal def-use record of the given instruction |inst|. This // method will update the use information of the operand ids of |inst|. The @@ -218,6 +215,7 @@ class DefUseManager { void UpdateDefUse(Instruction* inst); private: + using IdToUsersMap = std::set; using InstToUsedIdsMap = std::unordered_map>; diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp index cf74fa00..0f36812c 100644 --- a/source/opt/strip_reflect_info_pass.cpp +++ b/source/opt/strip_reflect_info_pass.cpp @@ -66,7 +66,7 @@ Pass::Status StripReflectInfoPass::Process() { const std::string ext_name = inst.GetInOperand(0).AsString(); if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { to_remove.push_back(&inst); - } else if (0 == std::strcmp(ext_name, "SPV_GOOGLE_user_type")) { + } else if (ext_name == "SPV_GOOGLE_user_type") { to_remove.push_back(&inst); } else if (!other_uses_for_decorate_string && ext_name == "SPV_GOOGLE_decorate_string") { diff --git a/test/opt/def_use_test.cpp b/test/opt/def_use_test.cpp index cfdad74a..431f3dc2 100644 --- a/test/opt/def_use_test.cpp +++ b/test/opt/def_use_test.cpp @@ -1707,9 +1707,10 @@ TEST_F(UpdateUsesTest, KeepOldUses) { def->SetInOperands({{SPV_OPERAND_TYPE_ID, {25}}}); context->UpdateDefUse(def); - auto users = def_use_mgr->id_to_users(); - UserEntry entry = {def, use}; - EXPECT_THAT(users, Contains(entry)); + auto scanUser = [&](Instruction* user) { return user != use; }; + bool userFound = !def_use_mgr->WhileEachUser(def, scanUser); + + EXPECT_TRUE(userFound); } // clang-format on -- cgit v1.2.3 From 31561588783687524176eaa2f828a345ec4ef944 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 10 Dec 2021 06:58:53 +1000 Subject: optimizer: restore previous ABI. (#4653) The change in commit 4ac8e5e541ea992dc6f44a4d4eb065a8fe0888ec Author: Greg Fischer Date: Wed Sep 15 12:38:34 2021 -0600 Add preserve_interface mode to aggressive_dead_code_elim (#4520) Broke the C++ ABI for spirv-tools shared libraries on Linux, for not a great reason. Restore the previous ABI. --- include/spirv-tools/optimizer.hpp | 3 ++- source/opt/optimizer.cpp | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 21059cbe..d9c511af 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -519,7 +519,8 @@ Optimizer::PassToken CreateDeadInsertElimPass(); // interface are considered live and are not eliminated. This mode is needed // by GPU-Assisted validation instrumentation, where a change in the interface // is not allowed. -Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface = false); +Optimizer::PassToken CreateAggressiveDCEPass(); +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface); // Creates a remove-unused-interface-variables pass. // Removes variables referenced on the |OpEntryPoint| instruction that are not diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index e74db26f..32a3d01e 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -764,6 +764,11 @@ Optimizer::PassToken CreateLocalMultiStoreElimPass() { MakeUnique()); } +Optimizer::PassToken CreateAggressiveDCEPass() { + return MakeUnique( + MakeUnique(false)); +} + Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) { return MakeUnique( MakeUnique(preserve_interface)); -- cgit v1.2.3 From e4527925944cbc8e91ef487c90c8e505e2065930 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Fri, 10 Dec 2021 15:06:23 +0000 Subject: Simplify the as fuzzer target (#4647) Makes the logic in the as fuzzer target closer to the logic of the spirv-as tool. Fixes #4643 --- test/fuzzers/spvtools_as_fuzzer.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp index ba3f5b97..8ead1cff 100644 --- a/test/fuzzers/spvtools_as_fuzzer.cpp +++ b/test/fuzzers/spvtools_as_fuzzer.cpp @@ -32,22 +32,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; } - std::vector input; - input.resize(size >> 2); - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - std::vector input_str; - size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char); - input_str.resize(char_count); - memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t)); + std::vector contents; + contents.resize(size); + memcpy(contents.data(), data, size); spv_binary binary = nullptr; spv_diagnostic diagnostic = nullptr; - spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + spvTextToBinaryWithOptions(context, contents.data(), contents.size(), SPV_TEXT_TO_BINARY_OPTION_NONE, &binary, &diagnostic); if (diagnostic) { @@ -61,7 +52,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { binary = nullptr; } - spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + spvTextToBinaryWithOptions(context, contents.data(), contents.size(), SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS, &binary, &diagnostic); if (diagnostic) { -- cgit v1.2.3 From ff07cfd86fa229525659f6b81058b3171a67bef1 Mon Sep 17 00:00:00 2001 From: SpaceIm <30052553+SpaceIm@users.noreply.github.com> Date: Fri, 10 Dec 2021 10:35:52 -0500 Subject: CMake iOS fixes: rely on CMAKE_SYSTEM_NAME and handle bundle installation (#4619) * detect iOS with CMAKE_SYSTEM_NAME IOS_PLATFORM is not a regular variable, it comes from unofficial iOS toolchain * fix installation of executables if BUNDLE enabled CMAKE_MACOSX_BUNDLE is enabled by default if CMAKE_SYSTEM_NAME is iOS, tvOS or watchOS. So installation of executables needs a BUNDLE DESTINATION otherwise configuration fails. --- tools/CMakeLists.txt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 6039089a..0a7e8651 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -44,7 +44,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - if (NOT DEFINED IOS_PLATFORM) # iOS does not allow std::system calls which spirv-reduce requires + if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) # iOS does not allow std::system calls which spirv-reduce requires add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY}) endif() add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY}) @@ -58,7 +58,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) ${SPIRV_HEADER_INCLUDE_DIR}) set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt spirv-cfg spirv-link spirv-lint) - if(NOT DEFINED IOS_PLATFORM) + if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce) endif() @@ -68,9 +68,6 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) endif(SPIRV_BUILD_FUZZER) if(ENABLE_SPIRV_TOOLS_INSTALL) - install(TARGETS ${SPIRV_INSTALL_TARGETS} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS ${SPIRV_INSTALL_TARGETS} DESTINATION ${CMAKE_INSTALL_BINDIR}) endif(ENABLE_SPIRV_TOOLS_INSTALL) endif() -- cgit v1.2.3 From f0351b7bc60b9e15492f8abc21f1a21aba8fcda6 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Mon, 13 Dec 2021 10:56:52 +0000 Subject: Avoid id bound errors during opt fuzzing (#4658) Use a very large id bound when fuzzing the optimizer, and check that the input does not ids that are too close to this bound. This should make it impossible in practice for an id overflow to occur. Fixes #4657. --- test/fuzzers/BUILD.gn | 3 + test/fuzzers/CMakeLists.txt | 6 +- test/fuzzers/spvtools_opt_fuzzer_common.cpp | 92 +++++++++++++++++++++++ test/fuzzers/spvtools_opt_fuzzer_common.h | 35 +++++++++ test/fuzzers/spvtools_opt_legalization_fuzzer.cpp | 33 ++------ test/fuzzers/spvtools_opt_performance_fuzzer.cpp | 33 ++------ test/fuzzers/spvtools_opt_size_fuzzer.cpp | 33 ++------ 7 files changed, 157 insertions(+), 78 deletions(-) create mode 100644 test/fuzzers/spvtools_opt_fuzzer_common.cpp create mode 100644 test/fuzzers/spvtools_opt_fuzzer_common.h diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn index 2aef4e8f..fc458a4f 100644 --- a/test/fuzzers/BUILD.gn +++ b/test/fuzzers/BUILD.gn @@ -87,18 +87,21 @@ spvtools_fuzzer("spvtools_dis_fuzzer_src") { spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") { sources = [ "spvtools_opt_performance_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", ] } spvtools_fuzzer("spvtools_opt_legalization_fuzzer_src") { sources = [ "spvtools_opt_legalization_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", ] } spvtools_fuzzer("spvtools_opt_size_fuzzer_src") { sources = [ "spvtools_opt_size_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", ] } diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt index 50c45895..e1fe516a 100644 --- a/test/fuzzers/CMakeLists.txt +++ b/test/fuzzers/CMakeLists.txt @@ -46,9 +46,9 @@ if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) if (${SPIRV_BUILD_FUZZER}) add_spvtools_libfuzzer_target(TARGET spvtools_fuzz_fuzzer SRCS spvtools_fuzz_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY}) diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.cpp b/test/fuzzers/spvtools_opt_fuzzer_common.cpp new file mode 100644 index 00000000..cf790641 --- /dev/null +++ b/test/fuzzers/spvtools_opt_fuzzer_common.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2021 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 "test/fuzzers/spvtools_opt_fuzzer_common.h" + +#include "source/opt/build_module.h" +#include "test/fuzzers/random_generator.h" + +namespace spvtools { +namespace fuzzers { + +int OptFuzzerTestOneInput( + const uint8_t* data, size_t size, + std::function register_passes) { + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + auto target_env = random_gen.GetTargetEnv(); + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + // The largest possible id bound is used when running the optimizer, to avoid + // the problem of id overflows. + const size_t kFinalIdLimit = UINT32_MAX; + + // The input is scanned to check that it does not already use an id too close + // to this limit. This still gives the optimizer a large set of ids to + // consume. It is thus very unlikely that id overflow will occur during + // fuzzing. If it does, then the initial id limit should be decreased. + const size_t kInitialIdLimit = kFinalIdLimit - 1000000U; + + // Build the module and scan it to check that all used ids are below the + // initial limit. + auto ir_context = + spvtools::BuildModule(target_env, nullptr, input.data(), input.size()); + if (ir_context == nullptr) { + // It was not possible to build a valid module; that's OK - skip this input. + return 0; + } + bool found_excessively_large_id = false; + ir_context->module()->ForEachInst( + [&found_excessively_large_id](spvtools::opt::Instruction* inst) -> void { + if (inst->result_id() && inst->result_id() > kInitialIdLimit) { + found_excessively_large_id = true; + } + }, + true); + if (found_excessively_large_id) { + // The input contains a very large id. The input is thus abandoned, to avoid + // the possibility of ending up hitting the id bound limit. + return 0; + } + + // Set the optimizer and its validator up with the largest possible id bound + // limit. + spvtools::ValidatorOptions validator_options; + spvtools::OptimizerOptions optimizer_options; + optimizer_options.set_max_id_bound(kFinalIdLimit); + validator_options.SetUniversalLimit(spv_validator_limit_max_id_bound, + kFinalIdLimit); + optimizer_options.set_validator_options(validator_options); + register_passes(optimizer); + optimizer.Run(input.data(), input.size(), &input, optimizer_options); + + return 0; +} + +} // namespace fuzzers +} // namespace spvtools diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.h b/test/fuzzers/spvtools_opt_fuzzer_common.h new file mode 100644 index 00000000..5f2f7922 --- /dev/null +++ b/test/fuzzers/spvtools_opt_fuzzer_common.h @@ -0,0 +1,35 @@ +// Copyright (c) 2021 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 TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ +#define TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ + +#include +#include +#include + +#include "spirv-tools/optimizer.hpp" + +namespace spvtools { +namespace fuzzers { + +// Helper function capturing the common logic for the various optimizer fuzzers. +int OptFuzzerTestOneInput( + const uint8_t* data, size_t size, + std::function register_passes); + +} // namespace fuzzers +} // namespace spvtools + +#endif // TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ diff --git a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp index 6f4d7e8b..fac4d23c 100644 --- a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp @@ -12,33 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include +#include +#include +#include #include "spirv-tools/optimizer.hpp" -#include "test/fuzzers/random_generator.h" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 1) { - return 0; - } - - spvtools::fuzzers::RandomGenerator random_gen(data, size); - spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterLegalizationPasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterLegalizationPasses(); + }); } diff --git a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp index 9c47d7d7..e6038b90 100644 --- a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp @@ -12,33 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include +#include +#include +#include #include "spirv-tools/optimizer.hpp" -#include "test/fuzzers/random_generator.h" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 1) { - return 0; - } - - spvtools::fuzzers::RandomGenerator random_gen(data, size); - spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterPerformancePasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterPerformancePasses(); + }); } diff --git a/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/test/fuzzers/spvtools_opt_size_fuzzer.cpp index 10fac42a..65492b1a 100644 --- a/test/fuzzers/spvtools_opt_size_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_size_fuzzer.cpp @@ -12,33 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include +#include +#include +#include #include "spirv-tools/optimizer.hpp" -#include "test/fuzzers/random_generator.h" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 1) { - return 0; - } - - spvtools::fuzzers::RandomGenerator random_gen(data, size); - spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterSizePasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterSizePasses(); + }); } -- cgit v1.2.3 From 4322c4b5a7dbde603d3febb172ab7ce7726b4b49 Mon Sep 17 00:00:00 2001 From: Shahbaz Youssefi Date: Tue, 14 Dec 2021 09:46:59 -0500 Subject: Refactor the disassembler code for reuse (#4616) The upcoming spirv-diff tool also outputs disassembly, although in a per-instruction basis. This change refactors the disassembler code to support such a use case. --- source/disassemble.cpp | 476 ++++++++++++++++++++++++++----------------------- source/disassemble.h | 59 ++++++ 2 files changed, 307 insertions(+), 228 deletions(-) diff --git a/source/disassemble.cpp b/source/disassemble.cpp index 250c2bf9..1d61b9f9 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -17,6 +17,8 @@ // This file contains a disassembler: It converts a SPIR-V binary // to text. +#include "source/disassemble.h" + #include #include #include @@ -28,9 +30,7 @@ #include "source/assembly_grammar.h" #include "source/binary.h" #include "source/diagnostic.h" -#include "source/disassemble.h" #include "source/ext_inst.h" -#include "source/name_mapper.h" #include "source/opcode.h" #include "source/parsed_operand.h" #include "source/print.h" @@ -40,29 +40,21 @@ #include "source/util/make_unique.h" #include "spirv-tools/libspirv.h" +namespace spvtools { namespace { // A Disassembler instance converts a SPIR-V binary to its assembly // representation. class Disassembler { public: - Disassembler(const spvtools::AssemblyGrammar& grammar, uint32_t options, - spvtools::NameMapper name_mapper) - : grammar_(grammar), - print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), - color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), - indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) - ? kStandardIndent - : 0), - comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + Disassembler(const AssemblyGrammar& grammar, uint32_t options, + NameMapper name_mapper) + : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), text_(), out_(print_ ? out_stream() : out_stream(text_)), - stream_(out_.get()), + instruction_disassembler_(grammar, out_.get(), options, name_mapper), header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)), - show_byte_offset_(spvIsInBitfield( - SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), - byte_offset_(0), - name_mapper_(std::move(name_mapper)) {} + byte_offset_(0) {} // Emits the assembly header for the module, and sets up internal state // so subsequent callbacks can handle the cases where the entire module @@ -78,56 +70,13 @@ class Disassembler { spv_result_t SaveTextResult(spv_text* text_result) const; private: - enum { kStandardIndent = 15 }; - - using out_stream = spvtools::out_stream; - - // Emits an operand for the given instruction, where the instruction - // is at offset words from the start of the binary. - void EmitOperand(const spv_parsed_instruction_t& inst, - const uint16_t operand_index); - - // Emits a mask expression for the given mask word of the specified type. - void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word); - - // Resets the output color, if color is turned on. - void ResetColor() { - if (color_) out_.get() << spvtools::clr::reset{print_}; - } - // Sets the output to grey, if color is turned on. - void SetGrey() { - if (color_) out_.get() << spvtools::clr::grey{print_}; - } - // Sets the output to blue, if color is turned on. - void SetBlue() { - if (color_) out_.get() << spvtools::clr::blue{print_}; - } - // Sets the output to yellow, if color is turned on. - void SetYellow() { - if (color_) out_.get() << spvtools::clr::yellow{print_}; - } - // Sets the output to red, if color is turned on. - void SetRed() { - if (color_) out_.get() << spvtools::clr::red{print_}; - } - // Sets the output to green, if color is turned on. - void SetGreen() { - if (color_) out_.get() << spvtools::clr::green{print_}; - } - - const spvtools::AssemblyGrammar& grammar_; const bool print_; // Should we also print to the standard output stream? - const bool color_; // Should we print in colour? - const int indent_; // How much to indent. 0 means don't indent - const int comment_; // Should we comment the source spv_endianness_t endian_; // The detected endianness of the binary. std::stringstream text_; // Captures the text, if not printing. out_stream out_; // The Output stream. Either to text_ or standard output. - std::ostream& stream_; // The output std::stream. - const bool header_; // Should we output header as the leading comment? - const bool show_byte_offset_; // Should we print byte offset, in hex? - size_t byte_offset_; // The number of bytes processed so far. - spvtools::NameMapper name_mapper_; + disassemble::InstructionDisassembler instruction_disassembler_; + const bool header_; // Should we output header as the leading comment? + size_t byte_offset_; // The number of bytes processed so far. bool inserted_decoration_space_ = false; bool inserted_debug_space_ = false; bool inserted_type_space_ = false; @@ -139,21 +88,11 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, endian_ = endian; if (header_) { - const char* generator_tool = - spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); - stream_ << "; SPIR-V\n" - << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." - << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n" - << "; Generator: " << generator_tool; - // For unknown tools, print the numeric tool value. - if (0 == strcmp("Unknown", generator_tool)) { - stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; - } - // Print the miscellaneous part of the generator word on the same - // line as the tool name. - stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n" - << "; Bound: " << id_bound << "\n" - << "; Schema: " << schema << "\n"; + instruction_disassembler_.EmitHeaderSpirv(); + instruction_disassembler_.EmitHeaderVersion(version); + instruction_disassembler_.EmitHeaderGenerator(generator); + instruction_disassembler_.EmitHeaderIdBound(id_bound); + instruction_disassembler_.EmitHeaderSchema(schema); } byte_offset_ = SPV_INDEX_INSTRUCTION * sizeof(uint32_t); @@ -163,31 +102,149 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, spv_result_t Disassembler::HandleInstruction( const spv_parsed_instruction_t& inst) { - auto opcode = static_cast(inst.opcode); - if (comment_ && opcode == SpvOpFunction) { - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; - } - if (comment_ && !inserted_decoration_space_ && - spvOpcodeIsDecoration(opcode)) { - inserted_decoration_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Annotations" << std::endl; + instruction_disassembler_.EmitSectionComment(inst, inserted_decoration_space_, + inserted_debug_space_, + inserted_type_space_); + + instruction_disassembler_.EmitInstruction(inst, byte_offset_); + + byte_offset_ += inst.num_words * sizeof(uint32_t); + + return SPV_SUCCESS; +} + +spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const { + if (!print_) { + size_t length = text_.str().size(); + char* str = new char[length + 1]; + if (!str) return SPV_ERROR_OUT_OF_MEMORY; + strncpy(str, text_.str().c_str(), length + 1); + spv_text text = new spv_text_t(); + if (!text) { + delete[] str; + return SPV_ERROR_OUT_OF_MEMORY; + } + text->str = str; + text->length = length; + *text_result = text; } - if (comment_ && !inserted_debug_space_ && spvOpcodeIsDebug(opcode)) { - inserted_debug_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Debug Information" << std::endl; + return SPV_SUCCESS; +} + +spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto disassembler = static_cast(user_data); + return disassembler->HandleHeader(endian, version, generator, id_bound, + schema); +} + +spv_result_t DisassembleInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto disassembler = static_cast(user_data); + return disassembler->HandleInstruction(*parsed_instruction); +} + +// Simple wrapper class to provide extra data necessary for targeted +// instruction disassembly. +class WrappedDisassembler { + public: + WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc) + : disassembler_(dis), inst_binary_(binary), word_count_(wc) {} + + Disassembler* disassembler() { return disassembler_; } + const uint32_t* inst_binary() const { return inst_binary_; } + size_t word_count() const { return word_count_; } + + private: + Disassembler* disassembler_; + const uint32_t* inst_binary_; + const size_t word_count_; +}; + +spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto wrapped = static_cast(user_data); + return wrapped->disassembler()->HandleHeader(endian, version, generator, + id_bound, schema); +} + +spv_result_t DisassembleTargetInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto wrapped = static_cast(user_data); + // Check if this is the instruction we want to disassemble. + if (wrapped->word_count() == parsed_instruction->num_words && + std::equal(wrapped->inst_binary(), + wrapped->inst_binary() + wrapped->word_count(), + parsed_instruction->words)) { + // Found the target instruction. Disassemble it and signal that we should + // stop searching so we don't output the same instruction again. + if (auto error = + wrapped->disassembler()->HandleInstruction(*parsed_instruction)) + return error; + return SPV_REQUESTED_TERMINATION; } - if (comment_ && !inserted_type_space_ && spvOpcodeGeneratesType(opcode)) { - inserted_type_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Types, variables and constants" << std::endl; + return SPV_SUCCESS; +} + +constexpr int kStandardIndent = 15; +} // namespace + +namespace disassemble { +InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar, + std::ostream& stream, + uint32_t options, + NameMapper name_mapper) + : grammar_(grammar), + stream_(stream), + print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), + color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), + indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) + ? kStandardIndent + : 0), + comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + show_byte_offset_( + spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), + name_mapper_(std::move(name_mapper)) {} + +void InstructionDisassembler::EmitHeaderSpirv() { stream_ << "; SPIR-V\n"; } + +void InstructionDisassembler::EmitHeaderVersion(uint32_t version) { + stream_ << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n"; +} + +void InstructionDisassembler::EmitHeaderGenerator(uint32_t generator) { + const char* generator_tool = + spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); + stream_ << "; Generator: " << generator_tool; + // For unknown tools, print the numeric tool value. + if (0 == strcmp("Unknown", generator_tool)) { + stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; } + // Print the miscellaneous part of the generator word on the same + // line as the tool name. + stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n"; +} + +void InstructionDisassembler::EmitHeaderIdBound(uint32_t id_bound) { + stream_ << "; Bound: " << id_bound << "\n"; +} + +void InstructionDisassembler::EmitHeaderSchema(uint32_t schema) { + stream_ << "; Schema: " << schema << "\n"; +} + +void InstructionDisassembler::EmitInstruction( + const spv_parsed_instruction_t& inst, size_t inst_byte_offset) { + auto opcode = static_cast(inst.opcode); if (inst.result_id) { SetBlue(); @@ -222,20 +279,45 @@ spv_result_t Disassembler::HandleInstruction( auto saved_flags = stream_.flags(); auto saved_fill = stream_.fill(); stream_ << " ; 0x" << std::setw(8) << std::hex << std::setfill('0') - << byte_offset_; + << inst_byte_offset; stream_.flags(saved_flags); stream_.fill(saved_fill); ResetColor(); } - - byte_offset_ += inst.num_words * sizeof(uint32_t); - stream_ << "\n"; - return SPV_SUCCESS; } -void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, - const uint16_t operand_index) { +void InstructionDisassembler::EmitSectionComment( + const spv_parsed_instruction_t& inst, bool& inserted_decoration_space, + bool& inserted_debug_space, bool& inserted_type_space) { + auto opcode = static_cast(inst.opcode); + if (comment_ && opcode == SpvOpFunction) { + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; + } + if (comment_ && !inserted_decoration_space && spvOpcodeIsDecoration(opcode)) { + inserted_decoration_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Annotations" << std::endl; + } + if (comment_ && !inserted_debug_space && spvOpcodeIsDebug(opcode)) { + inserted_debug_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Debug Information" << std::endl; + } + if (comment_ && !inserted_type_space && spvOpcodeGeneratesType(opcode)) { + inserted_type_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Types, variables and constants" << std::endl; + } +} + +void InstructionDisassembler::EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { assert(operand_index < inst.num_operands); const spv_parsed_operand_t& operand = inst.operands[operand_index]; const uint32_t word = inst.words[operand.offset]; @@ -277,7 +359,7 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, case SPV_OPERAND_TYPE_LITERAL_INTEGER: case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { SetRed(); - spvtools::EmitNumericLiteral(&stream_, inst, operand); + EmitNumericLiteral(&stream_, inst, operand); ResetColor(); } break; case SPV_OPERAND_TYPE_LITERAL_STRING: { @@ -360,8 +442,8 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, ResetColor(); } -void Disassembler::EmitMaskOperand(const spv_operand_type_t type, - const uint32_t word) { +void InstructionDisassembler::EmitMaskOperand(const spv_operand_type_t type, + const uint32_t word) { // Scan the mask from least significant bit to most significant bit. For each // set bit, emit the name of that bit. Separate multiple names with '|'. uint32_t remaining_word = word; @@ -387,88 +469,66 @@ void Disassembler::EmitMaskOperand(const spv_operand_type_t type, } } -spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const { - if (!print_) { - size_t length = text_.str().size(); - char* str = new char[length + 1]; - if (!str) return SPV_ERROR_OUT_OF_MEMORY; - strncpy(str, text_.str().c_str(), length + 1); - spv_text text = new spv_text_t(); - if (!text) { - delete[] str; - return SPV_ERROR_OUT_OF_MEMORY; - } - text->str = str; - text->length = length; - *text_result = text; - } - return SPV_SUCCESS; +void InstructionDisassembler::ResetColor() { + if (color_) stream_ << spvtools::clr::reset{print_}; } - -spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian, - uint32_t /* magic */, uint32_t version, - uint32_t generator, uint32_t id_bound, - uint32_t schema) { - assert(user_data); - auto disassembler = static_cast(user_data); - return disassembler->HandleHeader(endian, version, generator, id_bound, - schema); +void InstructionDisassembler::SetGrey() { + if (color_) stream_ << spvtools::clr::grey{print_}; } - -spv_result_t DisassembleInstruction( - void* user_data, const spv_parsed_instruction_t* parsed_instruction) { - assert(user_data); - auto disassembler = static_cast(user_data); - return disassembler->HandleInstruction(*parsed_instruction); +void InstructionDisassembler::SetBlue() { + if (color_) stream_ << spvtools::clr::blue{print_}; } +void InstructionDisassembler::SetYellow() { + if (color_) stream_ << spvtools::clr::yellow{print_}; +} +void InstructionDisassembler::SetRed() { + if (color_) stream_ << spvtools::clr::red{print_}; +} +void InstructionDisassembler::SetGreen() { + if (color_) stream_ << spvtools::clr::green{print_}; +} +} // namespace disassemble + +std::string spvInstructionBinaryToText(const spv_target_env env, + const uint32_t* instCode, + const size_t instWordCount, + const uint32_t* code, + const size_t wordCount, + const uint32_t options) { + spv_context context = spvContextCreate(env); + const AssemblyGrammar grammar(context); + if (!grammar.isValid()) { + spvContextDestroy(context); + return ""; + } -// Simple wrapper class to provide extra data necessary for targeted -// instruction disassembly. -class WrappedDisassembler { - public: - WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc) - : disassembler_(dis), inst_binary_(binary), word_count_(wc) {} - - Disassembler* disassembler() { return disassembler_; } - const uint32_t* inst_binary() const { return inst_binary_; } - size_t word_count() const { return word_count_; } - - private: - Disassembler* disassembler_; - const uint32_t* inst_binary_; - const size_t word_count_; -}; + // Generate friendly names for Ids if requested. + std::unique_ptr friendly_mapper; + NameMapper name_mapper = GetTrivialNameMapper(); + if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { + friendly_mapper = MakeUnique(context, code, wordCount); + name_mapper = friendly_mapper->GetNameMapper(); + } -spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian, - uint32_t /* magic */, uint32_t version, - uint32_t generator, uint32_t id_bound, - uint32_t schema) { - assert(user_data); - auto wrapped = static_cast(user_data); - return wrapped->disassembler()->HandleHeader(endian, version, generator, - id_bound, schema); -} + // Now disassemble! + Disassembler disassembler(grammar, options, name_mapper); + WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); + spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, + DisassembleTargetInstruction, nullptr); -spv_result_t DisassembleTargetInstruction( - void* user_data, const spv_parsed_instruction_t* parsed_instruction) { - assert(user_data); - auto wrapped = static_cast(user_data); - // Check if this is the instruction we want to disassemble. - if (wrapped->word_count() == parsed_instruction->num_words && - std::equal(wrapped->inst_binary(), - wrapped->inst_binary() + wrapped->word_count(), - parsed_instruction->words)) { - // Found the target instruction. Disassemble it and signal that we should - // stop searching so we don't output the same instruction again. - if (auto error = - wrapped->disassembler()->HandleInstruction(*parsed_instruction)) - return error; - return SPV_REQUESTED_TERMINATION; + spv_text text = nullptr; + std::string output; + if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { + output.assign(text->str, text->str + text->length); + // Drop trailing newline characters. + while (!output.empty() && output.back() == '\n') output.pop_back(); } - return SPV_SUCCESS; -} + spvTextDestroy(text); + spvContextDestroy(context); -} // namespace + return output; +} +} // namespace spvtools spv_result_t spvBinaryToText(const spv_const_context context, const uint32_t* code, const size_t wordCount, @@ -493,53 +553,13 @@ spv_result_t spvBinaryToText(const spv_const_context context, } // Now disassemble! - Disassembler disassembler(grammar, options, name_mapper); - if (auto error = spvBinaryParse(&hijack_context, &disassembler, code, - wordCount, DisassembleHeader, - DisassembleInstruction, pDiagnostic)) { + spvtools::Disassembler disassembler(grammar, options, name_mapper); + if (auto error = + spvBinaryParse(&hijack_context, &disassembler, code, wordCount, + spvtools::DisassembleHeader, + spvtools::DisassembleInstruction, pDiagnostic)) { return error; } return disassembler.SaveTextResult(pText); } - -std::string spvtools::spvInstructionBinaryToText(const spv_target_env env, - const uint32_t* instCode, - const size_t instWordCount, - const uint32_t* code, - const size_t wordCount, - const uint32_t options) { - spv_context context = spvContextCreate(env); - const spvtools::AssemblyGrammar grammar(context); - if (!grammar.isValid()) { - spvContextDestroy(context); - return ""; - } - - // Generate friendly names for Ids if requested. - std::unique_ptr friendly_mapper; - spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper(); - if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { - friendly_mapper = spvtools::MakeUnique( - context, code, wordCount); - name_mapper = friendly_mapper->GetNameMapper(); - } - - // Now disassemble! - Disassembler disassembler(grammar, options, name_mapper); - WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); - spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, - DisassembleTargetInstruction, nullptr); - - spv_text text = nullptr; - std::string output; - if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { - output.assign(text->str, text->str + text->length); - // Drop trailing newline characters. - while (!output.empty() && output.back() == '\n') output.pop_back(); - } - spvTextDestroy(text); - spvContextDestroy(context); - - return output; -} diff --git a/source/disassemble.h b/source/disassemble.h index ac357427..8eacb100 100644 --- a/source/disassemble.h +++ b/source/disassemble.h @@ -15,8 +15,10 @@ #ifndef SOURCE_DISASSEMBLE_H_ #define SOURCE_DISASSEMBLE_H_ +#include #include +#include "source/name_mapper.h" #include "spirv-tools/libspirv.h" namespace spvtools { @@ -33,6 +35,63 @@ std::string spvInstructionBinaryToText(const spv_target_env env, const size_t word_count, const uint32_t options); +class AssemblyGrammar; +namespace disassemble { + +// Shared code with other tools (than the disassembler) that might need to +// output disassembly. An InstructionDisassembler instance converts SPIR-V +// binary for an instruction to its assembly representation. +class InstructionDisassembler { + public: + InstructionDisassembler(const AssemblyGrammar& grammar, std::ostream& stream, + uint32_t options, NameMapper name_mapper); + + // Emits the assembly header for the module. + void EmitHeaderSpirv(); + void EmitHeaderVersion(uint32_t version); + void EmitHeaderGenerator(uint32_t generator); + void EmitHeaderIdBound(uint32_t id_bound); + void EmitHeaderSchema(uint32_t schema); + + // Emits the assembly text for the given instruction. + void EmitInstruction(const spv_parsed_instruction_t& inst, + size_t inst_byte_offset); + + // Emits a comment between different sections of the module. + void EmitSectionComment(const spv_parsed_instruction_t& inst, + bool& inserted_decoration_space, + bool& inserted_debug_space, + bool& inserted_type_space); + + // Resets the output color, if color is turned on. + void ResetColor(); + // Set the output color, if color is turned on. + void SetGrey(); + void SetBlue(); + void SetYellow(); + void SetRed(); + void SetGreen(); + + private: + // Emits an operand for the given instruction, where the instruction + // is at offset words from the start of the binary. + void EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + + // Emits a mask expression for the given mask word of the specified type. + void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word); + + const spvtools::AssemblyGrammar& grammar_; + std::ostream& stream_; + const bool print_; // Should we also print to the standard output stream? + const bool color_; // Should we print in colour? + const int indent_; // How much to indent. 0 means don't indent + const int comment_; // Should we comment the source + const bool show_byte_offset_; // Should we print byte offset, in hex? + spvtools::NameMapper name_mapper_; +}; + +} // namespace disassemble } // namespace spvtools #endif // SOURCE_DISASSEMBLE_H_ -- cgit v1.2.3 From 354a46a2a22e6de95ba72d5e318e742ddb808e95 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Wed, 15 Dec 2021 09:55:30 -0500 Subject: Rename strip reflect to strip nonsemantic (#4661) In https://github.com/KhronosGroup/SPIRV-Tools/pull/3110, the strip reflect pass was changed to also remove all explicitly nonsemantic instructions. This makes it so that the name of the pass no longer reflects what the pass actually does. This change renames the pass so that it reflects what the pass actaully does. --- Android.mk | 2 +- BUILD.gn | 4 +- include/spirv-tools/optimizer.hpp | 11 +- source/opt/CMakeLists.txt | 4 +- source/opt/optimizer.cpp | 8 +- source/opt/passes.h | 2 +- source/opt/strip_nonsemantic_info_pass.cpp | 131 ++++++++++++++++ source/opt/strip_nonsemantic_info_pass.h | 44 ++++++ source/opt/strip_reflect_info_pass.cpp | 131 ---------------- source/opt/strip_reflect_info_pass.h | 44 ------ test/opt/CMakeLists.txt | 2 +- test/opt/optimizer_test.cpp | 2 +- test/opt/strip_nonsemantic_info_test.cpp | 229 ++++++++++++++++++++++++++++ test/opt/strip_reflect_info_test.cpp | 231 ----------------------------- test/tools/opt/flags.py | 4 +- tools/opt/opt.cpp | 9 +- 16 files changed, 433 insertions(+), 425 deletions(-) create mode 100644 source/opt/strip_nonsemantic_info_pass.cpp create mode 100644 source/opt/strip_nonsemantic_info_pass.h delete mode 100644 source/opt/strip_reflect_info_pass.cpp delete mode 100644 source/opt/strip_reflect_info_pass.h create mode 100644 test/opt/strip_nonsemantic_info_test.cpp delete mode 100644 test/opt/strip_reflect_info_test.cpp diff --git a/Android.mk b/Android.mk index bc748e53..49af8fa8 100644 --- a/Android.mk +++ b/Android.mk @@ -168,7 +168,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/ssa_rewrite_pass.cpp \ source/opt/strength_reduction_pass.cpp \ source/opt/strip_debug_info_pass.cpp \ - source/opt/strip_reflect_info_pass.cpp \ + source/opt/strip_nonsemantic_info_pass.cpp \ source/opt/struct_cfg_analysis.cpp \ source/opt/type_manager.cpp \ source/opt/types.cpp \ diff --git a/BUILD.gn b/BUILD.gn index 309d5137..09176e6f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -738,8 +738,8 @@ static_library("spvtools_opt") { "source/opt/strength_reduction_pass.h", "source/opt/strip_debug_info_pass.cpp", "source/opt/strip_debug_info_pass.h", - "source/opt/strip_reflect_info_pass.cpp", - "source/opt/strip_reflect_info_pass.h", + "source/opt/strip_nonsemantic_info_pass.cpp", + "source/opt/strip_nonsemantic_info_pass.h", "source/opt/struct_cfg_analysis.cpp", "source/opt/struct_cfg_analysis.h", "source/opt/tree_iterator.h", diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index d9c511af..5d173c11 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -230,13 +230,14 @@ Optimizer::PassToken CreateNullPass(); // Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized. Optimizer::PassToken CreateStripDebugInfoPass(); -// Creates a strip-reflect-info pass. -// A strip-reflect-info pass removes all reflections instructions. -// For now, this is limited to removing decorations defined in -// SPV_GOOGLE_hlsl_functionality1. The coverage may expand in -// the future. +// [Deprecated] This will create a strip-nonsemantic-info pass. See below. Optimizer::PassToken CreateStripReflectInfoPass(); +// Creates a strip-nonsemantic-info pass. +// A strip-nonsemantic-info pass removes all reflections and explicitly +// non-semantic instructions. +Optimizer::PassToken CreateStripNonSemanticInfoPass(); + // Creates an eliminate-dead-functions pass. // An eliminate-dead-functions pass will remove all functions that are not in // the call trees rooted at entry points and exported functions. These diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 7d522fb5..f01766c7 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -111,7 +111,7 @@ set(SPIRV_TOOLS_OPT_SOURCES ssa_rewrite_pass.h strength_reduction_pass.h strip_debug_info_pass.h - strip_reflect_info_pass.h + strip_nonsemantic_info_pass.h struct_cfg_analysis.h tree_iterator.h type_manager.h @@ -218,7 +218,7 @@ set(SPIRV_TOOLS_OPT_SOURCES ssa_rewrite_pass.cpp strength_reduction_pass.cpp strip_debug_info_pass.cpp - strip_reflect_info_pass.cpp + strip_nonsemantic_info_pass.cpp struct_cfg_analysis.cpp type_manager.cpp types.cpp diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 32a3d01e..d8daf82c 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -288,6 +288,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateStripDebugInfoPass()); } else if (pass_name == "strip-reflect") { RegisterPass(CreateStripReflectInfoPass()); + } else if (pass_name == "strip-nonsemantic") { + RegisterPass(CreateStripNonSemanticInfoPass()); } else if (pass_name == "set-spec-const-default-value") { if (pass_args.size() > 0) { auto spec_ids_vals = @@ -653,8 +655,12 @@ Optimizer::PassToken CreateStripDebugInfoPass() { } Optimizer::PassToken CreateStripReflectInfoPass() { + return CreateStripNonSemanticInfoPass(); +} + +Optimizer::PassToken CreateStripNonSemanticInfoPass() { return MakeUnique( - MakeUnique()); + MakeUnique()); } Optimizer::PassToken CreateEliminateDeadFunctionsPass() { diff --git a/source/opt/passes.h b/source/opt/passes.h index f3c30d57..3266dbd0 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -74,7 +74,7 @@ #include "source/opt/ssa_rewrite_pass.h" #include "source/opt/strength_reduction_pass.h" #include "source/opt/strip_debug_info_pass.h" -#include "source/opt/strip_reflect_info_pass.h" +#include "source/opt/strip_nonsemantic_info_pass.h" #include "source/opt/unify_const_pass.h" #include "source/opt/upgrade_memory_model.h" #include "source/opt/vector_dce.h" diff --git a/source/opt/strip_nonsemantic_info_pass.cpp b/source/opt/strip_nonsemantic_info_pass.cpp new file mode 100644 index 00000000..9f522ae3 --- /dev/null +++ b/source/opt/strip_nonsemantic_info_pass.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strip_nonsemantic_info_pass.h" + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status StripNonSemanticInfoPass::Process() { + bool modified = false; + + std::vector to_remove; + + bool other_uses_for_decorate_string = false; + for (auto& inst : context()->module()->annotations()) { + switch (inst.opcode()) { + case SpvOpDecorateStringGOOGLE: + if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(1) == SpvDecorationUserTypeGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case SpvOpMemberDecorateStringGOOGLE: + if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(2) == SpvDecorationUserTypeGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case SpvOpDecorateId: + if (inst.GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + to_remove.push_back(&inst); + } + break; + + default: + break; + } + } + + for (auto& inst : context()->module()->extensions()) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { + to_remove.push_back(&inst); + } else if (ext_name == "SPV_GOOGLE_user_type") { + to_remove.push_back(&inst); + } else if (!other_uses_for_decorate_string && + ext_name == "SPV_GOOGLE_decorate_string") { + to_remove.push_back(&inst); + } else if (ext_name == "SPV_KHR_non_semantic_info") { + to_remove.push_back(&inst); + } + } + + // clear all debug data now if it hasn't been cleared already, to remove any + // remaining OpString that may have been referenced by non-semantic extinsts + for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg); + for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg); + for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg); + for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg); + + // remove any extended inst imports that are non semantic + std::unordered_set non_semantic_sets; + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.")) { + non_semantic_sets.insert(inst.result_id()); + to_remove.push_back(&inst); + } + } + + // if we removed some non-semantic sets, then iterate over the instructions in + // the module to remove any OpExtInst that referenced those sets + if (!non_semantic_sets.empty()) { + context()->module()->ForEachInst( + [&non_semantic_sets, &to_remove](Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) != + non_semantic_sets.end()) { + to_remove.push_back(inst); + } + } + }); + } + + // OpName must come first, since they may refer to other debug instructions. + // If they are after the instructions that refer to, then they will be killed + // when that instruction is killed, which will lead to a double kill. + std::sort(to_remove.begin(), to_remove.end(), + [](Instruction* lhs, Instruction* rhs) -> bool { + if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName) + return true; + return false; + }); + + for (auto* inst : to_remove) { + modified = true; + context()->KillInst(inst); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/strip_nonsemantic_info_pass.h b/source/opt/strip_nonsemantic_info_pass.h new file mode 100644 index 00000000..ff4e2e1d --- /dev/null +++ b/source/opt/strip_nonsemantic_info_pass.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ +#define SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StripNonSemanticInfoPass : public Pass { + public: + const char* name() const override { return "strip-nonsemantic"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp deleted file mode 100644 index 0f36812c..00000000 --- a/source/opt/strip_reflect_info_pass.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "source/opt/strip_reflect_info_pass.h" - -#include -#include - -#include "source/opt/instruction.h" -#include "source/opt/ir_context.h" -#include "source/util/string_utils.h" - -namespace spvtools { -namespace opt { - -Pass::Status StripReflectInfoPass::Process() { - bool modified = false; - - std::vector to_remove; - - bool other_uses_for_decorate_string = false; - for (auto& inst : context()->module()->annotations()) { - switch (inst.opcode()) { - case SpvOpDecorateStringGOOGLE: - if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE || - inst.GetSingleWordInOperand(1) == SpvDecorationUserTypeGOOGLE) { - to_remove.push_back(&inst); - } else { - other_uses_for_decorate_string = true; - } - break; - - case SpvOpMemberDecorateStringGOOGLE: - if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE || - inst.GetSingleWordInOperand(2) == SpvDecorationUserTypeGOOGLE) { - to_remove.push_back(&inst); - } else { - other_uses_for_decorate_string = true; - } - break; - - case SpvOpDecorateId: - if (inst.GetSingleWordInOperand(1) == - SpvDecorationHlslCounterBufferGOOGLE) { - to_remove.push_back(&inst); - } - break; - - default: - break; - } - } - - for (auto& inst : context()->module()->extensions()) { - const std::string ext_name = inst.GetInOperand(0).AsString(); - if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { - to_remove.push_back(&inst); - } else if (ext_name == "SPV_GOOGLE_user_type") { - to_remove.push_back(&inst); - } else if (!other_uses_for_decorate_string && - ext_name == "SPV_GOOGLE_decorate_string") { - to_remove.push_back(&inst); - } else if (ext_name == "SPV_KHR_non_semantic_info") { - to_remove.push_back(&inst); - } - } - - // clear all debug data now if it hasn't been cleared already, to remove any - // remaining OpString that may have been referenced by non-semantic extinsts - for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg); - for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg); - for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg); - for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg); - - // remove any extended inst imports that are non semantic - std::unordered_set non_semantic_sets; - for (auto& inst : context()->module()->ext_inst_imports()) { - assert(inst.opcode() == SpvOpExtInstImport && - "Expecting an import of an extension's instruction set."); - const std::string extension_name = inst.GetInOperand(0).AsString(); - if (spvtools::utils::starts_with(extension_name, "NonSemantic.")) { - non_semantic_sets.insert(inst.result_id()); - to_remove.push_back(&inst); - } - } - - // if we removed some non-semantic sets, then iterate over the instructions in - // the module to remove any OpExtInst that referenced those sets - if (!non_semantic_sets.empty()) { - context()->module()->ForEachInst( - [&non_semantic_sets, &to_remove](Instruction* inst) { - if (inst->opcode() == SpvOpExtInst) { - if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) != - non_semantic_sets.end()) { - to_remove.push_back(inst); - } - } - }); - } - - // OpName must come first, since they may refer to other debug instructions. - // If they are after the instructions that refer to, then they will be killed - // when that instruction is killed, which will lead to a double kill. - std::sort(to_remove.begin(), to_remove.end(), - [](Instruction* lhs, Instruction* rhs) -> bool { - if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName) - return true; - return false; - }); - - for (auto* inst : to_remove) { - modified = true; - context()->KillInst(inst); - } - - return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; -} - -} // namespace opt -} // namespace spvtools diff --git a/source/opt/strip_reflect_info_pass.h b/source/opt/strip_reflect_info_pass.h deleted file mode 100644 index 4e1999ed..00000000 --- a/source/opt/strip_reflect_info_pass.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ -#define SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ - -#include "source/opt/ir_context.h" -#include "source/opt/module.h" -#include "source/opt/pass.h" - -namespace spvtools { -namespace opt { - -// See optimizer.hpp for documentation. -class StripReflectInfoPass : public Pass { - public: - const char* name() const override { return "strip-reflect"; } - Status Process() override; - - // Return the mask of preserved Analyses. - IRContext::Analysis GetPreservedAnalyses() override { - return IRContext::kAnalysisInstrToBlockMapping | - IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | - IRContext::kAnalysisDominatorAnalysis | - IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | - IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; - } -}; - -} // namespace opt -} // namespace spvtools - -#endif // SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index bc44e8d6..d70b9235 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -93,7 +93,7 @@ add_spvtools_unittest(TARGET opt simplification_test.cpp strength_reduction_test.cpp strip_debug_info_test.cpp - strip_reflect_info_test.cpp + strip_nonsemantic_info_test.cpp struct_cfg_analysis_test.cpp type_manager_test.cpp types_test.cpp diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp index a51638a1..0171c09b 100644 --- a/test/opt/optimizer_test.cpp +++ b/test/opt/optimizer_test.cpp @@ -147,7 +147,7 @@ TEST(Optimizer, CanRegisterPassesFromFlags) { std::vector pass_flags = { "--strip-debug", - "--strip-reflect", + "--strip-nonsemantic", "--set-spec-const-default-value=23:42 21:12", "--if-conversion", "--freeze-spec-const", diff --git a/test/opt/strip_nonsemantic_info_test.cpp b/test/opt/strip_nonsemantic_info_test.cpp new file mode 100644 index 00000000..0756d94f --- /dev/null +++ b/test/opt/strip_nonsemantic_info_test.cpp @@ -0,0 +1,229 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using StripNonSemanticInfoTest = PassTest<::testing::Test>; + +// This test acts as an end-to-end code example on how to strip +// reflection info from a SPIR-V module. Use this code pattern +// when you have compiled HLSL code with Glslang or DXC using +// option -fhlsl_functionality1 to insert reflection information, +// but then want to filter out the extra instructions before sending +// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1. +TEST_F(StripNonSemanticInfoTest, StripReflectEnd2EndExample) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::vector binary_in; + tools.Assemble(before, &binary_in); + + // Instantiate the optimizer, and run the strip-nonsemantic-info + // pass over the |binary_in| module, and place the modified module + // into |binary_out|. + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1); + optimizer.RegisterPass(spvtools::CreateStripNonSemanticInfoPass()); + std::vector binary_out; + optimizer.Run(binary_in.data(), binary_in.size(), &binary_out); + + // Check results + std::string disassembly; + tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + EXPECT_THAT(disassembly, testing::Eq(after)); +} + +// This test is functionally the same as the end-to-end test above, +// but uses the test SinglePassRunAndCheck test fixture instead. +TEST_F(StripNonSemanticInfoTest, StripHlslSemantic) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripNonSemanticInfoTest, StripHlslCounterBuffer) { + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateId %void HlslCounterBufferGOOGLE %float +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripNonSemanticInfoTest, StripHlslSemanticOnMember) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpMemberDecorateStringGOOGLE %struct 0 HlslSemanticGOOGLE "foobar" +%float = OpTypeFloat 32 +%_struct_3 = OpTypeStruct %float +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%float = OpTypeFloat 32 +%_struct_3 = OpTypeStruct %float +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticImport) { + std::string text = R"( +; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info" +; CHECK-NOT: OpExtInstImport +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticGlobal) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%1 = OpExtInst %void %ext 1 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticInFunction) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%1 = OpExtInst %void %ext 1 %foo +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticAfterFunction) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%1 = OpExtInst %void %ext 1 %foo +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticBetweenFunctions) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%1 = OpExtInst %void %ext 1 %foo +%bar = OpFunction %void None %void_fn +%bar_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/strip_reflect_info_test.cpp b/test/opt/strip_reflect_info_test.cpp deleted file mode 100644 index f3fc115a..00000000 --- a/test/opt/strip_reflect_info_test.cpp +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include "gmock/gmock.h" - -#include "spirv-tools/optimizer.hpp" - -#include "test/opt/pass_fixture.h" -#include "test/opt/pass_utils.h" - -namespace spvtools { -namespace opt { -namespace { - -using StripLineReflectInfoTest = PassTest<::testing::Test>; -using StripNonSemanticInfoTest = PassTest<::testing::Test>; - -// This test acts as an end-to-end code example on how to strip -// reflection info from a SPIR-V module. Use this code pattern -// when you have compiled HLSL code with Glslang or DXC using -// option -fhlsl_functionality1 to insert reflection information, -// but then want to filter out the extra instructions before sending -// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1. -TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) { - // This is a non-sensical example, but exercises the instructions. - std::string before = R"(OpCapability Shader -OpCapability Linkage -OpExtension "SPV_GOOGLE_decorate_string" -OpExtension "SPV_GOOGLE_hlsl_functionality1" -OpMemoryModel Logical Simple -OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" -OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" -%void = OpTypeVoid -%float = OpTypeFloat 32 -)"; - SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); - std::vector binary_in; - tools.Assemble(before, &binary_in); - - // Instantiate the optimizer, and run the strip-reflection-info - // pass over the |binary_in| module, and place the modified module - // into |binary_out|. - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1); - optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass()); - std::vector binary_out; - optimizer.Run(binary_in.data(), binary_in.size(), &binary_out); - - // Check results - std::string disassembly; - tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); - std::string after = R"(OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical Simple -%void = OpTypeVoid -%float = OpTypeFloat 32 -)"; - EXPECT_THAT(disassembly, testing::Eq(after)); -} - -// This test is functionally the same as the end-to-end test above, -// but uses the test SinglePassRunAndCheck test fixture instead. -TEST_F(StripLineReflectInfoTest, StripHlslSemantic) { - // This is a non-sensical example, but exercises the instructions. - std::string before = R"(OpCapability Shader -OpCapability Linkage -OpExtension "SPV_GOOGLE_decorate_string" -OpExtension "SPV_GOOGLE_hlsl_functionality1" -OpMemoryModel Logical Simple -OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" -OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" -%void = OpTypeVoid -%float = OpTypeFloat 32 -)"; - std::string after = R"(OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical Simple -%void = OpTypeVoid -%float = OpTypeFloat 32 -)"; - - SinglePassRunAndCheck(before, after, false); -} - -TEST_F(StripLineReflectInfoTest, StripHlslCounterBuffer) { - std::string before = R"(OpCapability Shader -OpCapability Linkage -OpExtension "SPV_GOOGLE_hlsl_functionality1" -OpMemoryModel Logical Simple -OpDecorateId %void HlslCounterBufferGOOGLE %float -%void = OpTypeVoid -%float = OpTypeFloat 32 -)"; - std::string after = R"(OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical Simple -%void = OpTypeVoid -%float = OpTypeFloat 32 -)"; - - SinglePassRunAndCheck(before, after, false); -} - -TEST_F(StripLineReflectInfoTest, StripHlslSemanticOnMember) { - // This is a non-sensical example, but exercises the instructions. - std::string before = R"(OpCapability Shader -OpCapability Linkage -OpExtension "SPV_GOOGLE_decorate_string" -OpExtension "SPV_GOOGLE_hlsl_functionality1" -OpMemoryModel Logical Simple -OpMemberDecorateStringGOOGLE %struct 0 HlslSemanticGOOGLE "foobar" -%float = OpTypeFloat 32 -%_struct_3 = OpTypeStruct %float -)"; - std::string after = R"(OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical Simple -%float = OpTypeFloat 32 -%_struct_3 = OpTypeStruct %float -)"; - - SinglePassRunAndCheck(before, after, false); -} - -TEST_F(StripNonSemanticInfoTest, StripNonSemanticImport) { - std::string text = R"( -; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info" -; CHECK-NOT: OpExtInstImport -OpCapability Shader -OpCapability Linkage -OpExtension "SPV_KHR_non_semantic_info" -%ext = OpExtInstImport "NonSemantic.Test" -OpMemoryModel Logical GLSL450 -)"; - - SinglePassRunAndMatch(text, true); -} - -TEST_F(StripNonSemanticInfoTest, StripNonSemanticGlobal) { - std::string text = R"( -; CHECK-NOT: OpExtInst -OpCapability Shader -OpCapability Linkage -OpExtension "SPV_KHR_non_semantic_info" -%ext = OpExtInstImport "NonSemantic.Test" -OpMemoryModel Logical GLSL450 -%void = OpTypeVoid -%1 = OpExtInst %void %ext 1 -)"; - - SinglePassRunAndMatch(text, true); -} - -TEST_F(StripNonSemanticInfoTest, StripNonSemanticInFunction) { - std::string text = R"( -; CHECK-NOT: OpExtInst -OpCapability Shader -OpCapability Linkage -OpExtension "SPV_KHR_non_semantic_info" -%ext = OpExtInstImport "NonSemantic.Test" -OpMemoryModel Logical GLSL450 -%void = OpTypeVoid -%void_fn = OpTypeFunction %void -%foo = OpFunction %void None %void_fn -%entry = OpLabel -%1 = OpExtInst %void %ext 1 %foo -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndMatch(text, true); -} - -TEST_F(StripNonSemanticInfoTest, StripNonSemanticAfterFunction) { - std::string text = R"( -; CHECK-NOT: OpExtInst -OpCapability Shader -OpCapability Linkage -OpExtension "SPV_KHR_non_semantic_info" -%ext = OpExtInstImport "NonSemantic.Test" -OpMemoryModel Logical GLSL450 -%void = OpTypeVoid -%void_fn = OpTypeFunction %void -%foo = OpFunction %void None %void_fn -%entry = OpLabel -OpReturn -OpFunctionEnd -%1 = OpExtInst %void %ext 1 %foo -)"; - - SinglePassRunAndMatch(text, true); -} - -TEST_F(StripNonSemanticInfoTest, StripNonSemanticBetweenFunctions) { - std::string text = R"( -; CHECK-NOT: OpExtInst -OpCapability Shader -OpCapability Linkage -OpExtension "SPV_KHR_non_semantic_info" -%ext = OpExtInstImport "NonSemantic.Test" -OpMemoryModel Logical GLSL450 -%void = OpTypeVoid -%void_fn = OpTypeFunction %void -%foo = OpFunction %void None %void_fn -%entry = OpLabel -OpReturn -OpFunctionEnd -%1 = OpExtInst %void %ext 1 %foo -%bar = OpFunction %void None %void_fn -%bar_entry = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndMatch(text, true); -} - -} // namespace -} // namespace opt -} // namespace spvtools diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index c79f6807..1486760d 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py @@ -72,7 +72,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, '--private-to-local', '--reduce-load-size', '--redundancy-elimination', '--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite', '--scalar-replacement', '--scalar-replacement=42', '--strength-reduction', - '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209', + '--strip-debug', '--strip-nonsemantic', '--vector-dce', '--workaround-1209', '--unify-const', '--graphics-robust-access', '--wrap-opkill', '--amd-ext-to-khr' ] expected_passes = [ @@ -117,7 +117,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, 'scalar-replacement=42', 'strength-reduction', 'strip-debug', - 'strip-reflect', + 'strip-nonsemantic', 'vector-dce', 'workaround-1209', 'unify-const', diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 04f81b8c..f60d1104 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -479,10 +479,13 @@ Options (in lexicographical order):)", --strip-debug Remove all debug instructions.)"); printf(R"( + --strip-nonsemantic + Remove all reflection and nonsemantic information.)"); + printf(R"( --strip-reflect - Remove all reflection information. For now, this covers - reflection information defined by SPV_GOOGLE_hlsl_functionality1 - and SPV_KHR_non_semantic_info)"); + DEPRECATED. Remove all reflection information. For now, this + covers reflection information defined by + SPV_GOOGLE_hlsl_functionality1 and SPV_KHR_non_semantic_info)"); printf(R"( --target-env= Set the target environment. Without this flag the target -- cgit v1.2.3 From b7251d4fb756be9a2b6b446c30bd9df0320e6d52 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Wed, 15 Dec 2021 11:06:51 -0500 Subject: reflect debug (#4662) The pass to remove the nonsemantic information and instructions is used for drivers or tools that may not support them. Debug information was only partially handle, which is causing a problem. We need to either fully remove debug information or not remove it all. Since I can see it being useful to keep the debug information even when the nonsemantic instructions are removed, I propose we do not remove debug info. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4269 --- source/opt/strip_nonsemantic_info_pass.cpp | 20 +-------- test/opt/strip_nonsemantic_info_test.cpp | 67 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/source/opt/strip_nonsemantic_info_pass.cpp b/source/opt/strip_nonsemantic_info_pass.cpp index 9f522ae3..cd1fbb63 100644 --- a/source/opt/strip_nonsemantic_info_pass.cpp +++ b/source/opt/strip_nonsemantic_info_pass.cpp @@ -76,13 +76,6 @@ Pass::Status StripNonSemanticInfoPass::Process() { } } - // clear all debug data now if it hasn't been cleared already, to remove any - // remaining OpString that may have been referenced by non-semantic extinsts - for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg); - for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg); - for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg); - for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg); - // remove any extended inst imports that are non semantic std::unordered_set non_semantic_sets; for (auto& inst : context()->module()->ext_inst_imports()) { @@ -106,19 +99,10 @@ Pass::Status StripNonSemanticInfoPass::Process() { to_remove.push_back(inst); } } - }); + }, + true); } - // OpName must come first, since they may refer to other debug instructions. - // If they are after the instructions that refer to, then they will be killed - // when that instruction is killed, which will lead to a double kill. - std::sort(to_remove.begin(), to_remove.end(), - [](Instruction* lhs, Instruction* rhs) -> bool { - if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName) - return true; - return false; - }); - for (auto* inst : to_remove) { modified = true; context()->KillInst(inst); diff --git a/test/opt/strip_nonsemantic_info_test.cpp b/test/opt/strip_nonsemantic_info_test.cpp index 0756d94f..3aacffa3 100644 --- a/test/opt/strip_nonsemantic_info_test.cpp +++ b/test/opt/strip_nonsemantic_info_test.cpp @@ -224,6 +224,73 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +// Make sure that strip reflect does not remove the debug info (OpString and +// OpLine). +TEST_F(StripNonSemanticInfoTest, DontStripDebug) { + std::string text = R"(OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpString "file" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%1 = OpFunction %void None %4 +%5 = OpLabel +OpLine %2 1 1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(StripNonSemanticInfoTest, RemovedNonSemanticDebugInfo) { + const std::string text = R"( +;CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info +;CHECK-NOT: OpExtInstImport "NonSemantic.Shader.DebugInfo.100 +;CHECK-NOT: OpExtInst %void {{%\w+}} DebugSource +;CHECK-NOT: OpExtInst %void {{%\w+}} DebugLine + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_COLOR %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + %5 = OpString "t.hlsl" + %6 = OpString "float" + %7 = OpString "color" + %8 = OpString "PSInput" + %9 = OpString "PSMain" + %10 = OpString "" + %11 = OpString "input" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_1 = OpConstant %uint 1 + %uint_9 = OpConstant %uint 9 + %21 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %13 = OpExtInst %void %1 DebugSource %5 + %PSMain = OpFunction %void None %21 + %22 = OpLabel + %23 = OpLoad %v4float %in_var_COLOR + OpStore %out_var_SV_TARGET %23 + %24 = OpExtInst %void %1 DebugLine %13 %uint_9 %uint_9 %uint_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + SinglePassRunAndMatch(text, true); +} + } // namespace } // namespace opt } // namespace spvtools -- cgit v1.2.3 From 7d768812e20296c877a44ce0633d71f952fbf83c Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 15 Dec 2021 14:38:28 -0500 Subject: Basic support for SPIR-V 1.6 (#4663) * Basic support for SPIR-V 1.6 * Update SPIRV-Headers deps * Add new environment enum for SPIR-V 1.6 * Make default environment 1.6 for most tools * Update tests * Disallow conditional branch with duplicate labels * Disallow Dim=Buffer with sampled images * Do not require the non-semantic extension after SPIR-V 1.5 --- DEPS | 2 +- include/spirv-tools/libspirv.h | 2 + source/ext_inst.cpp | 1 + source/spirv_target_env.cpp | 18 ++- source/table.cpp | 1 + source/val/validate_cfg.cpp | 7 ++ source/val/validate_extensions.cpp | 3 +- source/val/validate_image.cpp | 13 ++ source/val/validation_state.cpp | 14 ++- test/binary_header_get_test.cpp | 4 +- test/target_env_test.cpp | 8 +- test/text_to_binary.extension_test.cpp | 37 +++--- test/tools/expect.py | 15 +++ test/tools/opt/flags.py | 10 +- test/val/val_cfg_test.cpp | 45 +++++++ test/val/val_ext_inst_test.cpp | 12 ++ .../val_extension_spv_khr_terminate_invocation.cpp | 29 ++++- test/val/val_image_test.cpp | 18 +++ test/val/val_non_semantic_test.cpp | 22 +++- test/val/val_version_test.cpp | 136 ++++++++++++++++++++- tools/as/as.cpp | 2 +- tools/cfg/cfg.cpp | 2 +- tools/link/linker.cpp | 2 +- tools/lint/lint.cpp | 2 +- tools/opt/opt.cpp | 2 +- tools/reduce/reduce.cpp | 2 +- tools/val/val.cpp | 8 +- 27 files changed, 362 insertions(+), 55 deletions(-) diff --git a/DEPS b/DEPS index 2aaf11f9..88cb4cad 100644 --- a/DEPS +++ b/DEPS @@ -6,7 +6,7 @@ vars = { 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133', 'googletest_revision': 'bf0701daa9f5b30e5882e2f8f9a5280bcba87e77', 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', - 'spirv_headers_revision': '814e728b30ddd0f4509233099a3ad96fd4318c07', + 'spirv_headers_revision': 'eddd4dfc930f1374a70797460240a501c7d333f7', } deps = { diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 8df14f5f..9d7de17e 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -516,6 +516,8 @@ typedef enum { SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions. SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. + + SPV_ENV_UNIVERSAL_1_6, // SPIR-V 1.6 latest revision, no other restrictions. SPV_ENV_MAX // Keep this as the last enum value. } spv_target_env; diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 812053ec..37b423ee 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -96,6 +96,7 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: *pExtInstTable = &kTable_1_0; return SPV_SUCCESS; default: diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index 187ab61e..43d1c3dd 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -72,6 +72,8 @@ const char* spvTargetEnvDescription(spv_target_env env) { return "SPIR-V 1.5"; case SPV_ENV_VULKAN_1_2: return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; + case SPV_ENV_UNIVERSAL_1_6: + return "SPIR-V 1.6"; case SPV_ENV_MAX: assert(false && "Invalid target environment value."); break; @@ -113,6 +115,8 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: return SPV_SPIRV_VERSION_WORD(1, 5); + case SPV_ENV_UNIVERSAL_1_6: + return SPV_SPIRV_VERSION_WORD(1, 6); case SPV_ENV_MAX: assert(false && "Invalid target environment value."); break; @@ -131,6 +135,7 @@ static const std::pair spvTargetEnvNameMap[] = { {"spv1.3", SPV_ENV_UNIVERSAL_1_3}, {"spv1.4", SPV_ENV_UNIVERSAL_1_4}, {"spv1.5", SPV_ENV_UNIVERSAL_1_5}, + {"spv1.6", SPV_ENV_UNIVERSAL_1_6}, {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2}, {"opencl1.2", SPV_ENV_OPENCL_1_2}, {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0}, @@ -211,6 +216,7 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: return false; case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: @@ -244,6 +250,7 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: return false; case SPV_ENV_OPENCL_1_2: case SPV_ENV_OPENCL_EMBEDDED_1_2: @@ -284,6 +291,7 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: return false; case SPV_ENV_OPENGL_4_0: case SPV_ENV_OPENGL_4_1: @@ -321,6 +329,7 @@ bool spvIsValidEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: case SPV_ENV_OPENGL_4_0: case SPV_ENV_OPENGL_4_1: case SPV_ENV_OPENGL_4_2: @@ -355,16 +364,17 @@ std::string spvLogStringForEnv(spv_target_env env) { } case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: - case SPV_ENV_VULKAN_1_1_SPIRV_1_4: { - case SPV_ENV_VULKAN_1_2: - return "Vulkan"; + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: { + return "Vulkan"; } case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_UNIVERSAL_1_4: - case SPV_ENV_UNIVERSAL_1_5: { + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: { return "Universal"; } case SPV_ENV_WEBGPU_0: diff --git a/source/table.cpp b/source/table.cpp index d4a2d7e9..a87bbf28 100644 --- a/source/table.cpp +++ b/source/table.cpp @@ -41,6 +41,7 @@ spv_context spvContextCreate(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: break; default: return nullptr; diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index 7842e56d..26b2e94a 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -27,6 +27,7 @@ #include "source/cfa.h" #include "source/opcode.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/spirv_validator_options.h" #include "source/val/basic_block.h" @@ -191,6 +192,12 @@ spv_result_t ValidateBranchConditional(ValidationState_t& _, "ID of an OpLabel instruction"; } + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && true_id == false_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, True Label and False Label must be " + "different labels"; + } + return SPV_SUCCESS; } diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index 479e9e41..b9f8e3c5 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -739,7 +739,8 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { const auto name_id = 1; - if (!_.HasExtension(kSPV_KHR_non_semantic_info)) { + if (_.version() <= SPV_SPIRV_VERSION_WORD(1, 5) && + !_.HasExtension(kSPV_KHR_non_semantic_info)) { const std::string name = inst->GetOperandAs(name_id); if (name.find("NonSemantic.") == 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 59000a2d..037fab69 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -20,6 +20,7 @@ #include "source/diagnostic.h" #include "source/opcode.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/bitutils.h" #include "source/val/instruction.h" @@ -71,6 +72,7 @@ bool CheckAllImageOperandsHandled() { // blocks other PRs. // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565 case SpvImageOperandsOffsetsMask: + case SpvImageOperandsNontemporalMask: return true; } return false; @@ -630,6 +632,10 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // TODO: add validation } + if (mask & SpvImageOperandsNontemporalMask) { + // Checked elsewhere: SPIR-V 1.6 version or later. + } + return SPV_SUCCESS; } @@ -915,6 +921,13 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _, "operand set to 0 or 1"; } + // This covers both OpTypeSampledImage and OpSampledImage. + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && info.dim == SpvDimBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, sampled image dimension must not be " + "Buffer"; + } + return SPV_SUCCESS; } diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 9d708be4..f127654e 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -175,8 +175,18 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, } } - // LocalSizeId is always allowed in non-Vulkan environments. - features_.env_allow_localsizeid = !spvIsVulkanEnv(env); + // LocalSizeId is only disallowed without maintainence4. + switch (env) { + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + features_.env_allow_localsizeid = false; + break; + default: + features_.env_allow_localsizeid = true; + break; + } // Only attempt to count if we have words, otherwise let the other validation // fail and generate an error. diff --git a/test/binary_header_get_test.cpp b/test/binary_header_get_test.cpp index 3ce0b63a..f94f0c1a 100644 --- a/test/binary_header_get_test.cpp +++ b/test/binary_header_get_test.cpp @@ -51,8 +51,8 @@ TEST_F(BinaryHeaderGet, Default) { ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header)); ASSERT_EQ(static_cast(SpvMagicNumber), header.magic); - // Expect SPIRV-Headers updated to SPIR-V 1.5. - ASSERT_EQ(0x00010500u, header.version); + // Expect SPIRV-Headers updated to SPIR-V 1.6. + ASSERT_EQ(0x00010600u, header.version); ASSERT_EQ(static_cast(SPV_GENERATOR_CODEPLAY), header.generator); ASSERT_EQ(1u, header.bound); ASSERT_EQ(0u, header.schema); diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp index 4acb8ff2..c5646b8f 100644 --- a/test/target_env_test.cpp +++ b/test/target_env_test.cpp @@ -135,7 +135,7 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 0), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 0), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.1 cases {VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1}, {VK(1, 1), SPV(1, 1), true, SPV_ENV_VULKAN_1_1}, @@ -143,7 +143,7 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 1), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 1), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.2 cases {VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 1), true, SPV_ENV_VULKAN_1_2}, @@ -151,9 +151,7 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 2), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, - // Vulkan 1.3 cases - {VK(1, 3), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 2), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 2.0 cases {VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 99.0 cases diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 1324206c..1f3ff243 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -957,61 +957,62 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( SPV_KHR_integer_dot_product, ExtensionRoundTripTest, Combine( - Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, - SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, + SPV_ENV_VULKAN_1_2), ValuesIn(std::vector{ {"OpExtension \"SPV_KHR_integer_dot_product\"\n", MakeInstruction(SpvOpExtension, MakeVector("SPV_KHR_integer_dot_product"))}, - {"OpCapability DotProductInputAllKHR\n", + {"OpCapability DotProductInputAll\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductInputAllKHR})}, - {"OpCapability DotProductInput4x8BitKHR\n", + {"OpCapability DotProductInput4x8Bit\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductInput4x8BitKHR})}, - {"OpCapability DotProductInput4x8BitPackedKHR\n", + {"OpCapability DotProductInput4x8BitPacked\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductInput4x8BitPackedKHR})}, - {"OpCapability DotProductKHR\n", + {"OpCapability DotProduct\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductKHR})}, - {"%2 = OpSDotKHR %1 %3 %4\n", + {"%2 = OpSDot %1 %3 %4\n", MakeInstruction(SpvOpSDotKHR, {1, 2, 3, 4})}, - {"%2 = OpSDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSDot %1 %3 %4 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSDotKHR, {1, 2, 3, 4, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpUDotKHR %1 %3 %4\n", + {"%2 = OpUDot %1 %3 %4\n", MakeInstruction(SpvOpUDotKHR, {1, 2, 3, 4})}, - {"%2 = OpUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpUDot %1 %3 %4 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpUDotKHR, {1, 2, 3, 4, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpSUDotKHR %1 %3 %4\n", + {"%2 = OpSUDot %1 %3 %4\n", MakeInstruction(SpvOpSUDotKHR, {1, 2, 3, 4})}, - {"%2 = OpSUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSUDot %1 %3 %4 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSUDotKHR, {1, 2, 3, 4, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpSDotAccSatKHR %1 %3 %4 %5\n", + {"%2 = OpSDotAccSat %1 %3 %4 %5\n", MakeInstruction(SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5})}, - {"%2 = OpSDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpUDotAccSatKHR %1 %3 %4 %5\n", + {"%2 = OpUDotAccSat %1 %3 %4 %5\n", MakeInstruction(SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5})}, - {"%2 = OpUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5\n", + {"%2 = OpSUDotAccSat %1 %3 %4 %5\n", MakeInstruction(SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5})}, - {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5, diff --git a/test/tools/expect.py b/test/tools/expect.py index 0b51adc9..7351c029 100755 --- a/test/tools/expect.py +++ b/test/tools/expect.py @@ -285,6 +285,21 @@ class ValidObjectFile1_5(ReturnCodeIsZero, CorrectObjectFilePreamble): return True, '' +class ValidObjectFile1_6(ReturnCodeIsZero, CorrectObjectFilePreamble): + """Mixin class for checking that every input file generates a valid SPIR-V 1.6 + object file following the object file naming rule, and there is no output on + stdout/stderr.""" + + def check_object_file_preamble(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename), 0x10600) + if not success: + return False, message + return True, '' + + class ValidObjectFileWithAssemblySubstr(SuccessfulReturn, CorrectObjectFilePreamble): """Mixin class for checking that every input file generates a valid object diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index 1486760d..52a43c51 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py @@ -34,7 +34,7 @@ def empty_main_assembly(): @inside_spirv_testsuite('SpirvOptBase') -class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_5): +class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_6): """Tests that spirv-opt accepts a SPIR-V object file.""" shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') @@ -52,7 +52,7 @@ class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch): @inside_spirv_testsuite('SpirvOptFlags') -class TestValidPassFlags(expect.ValidObjectFile1_5, +class TestValidPassFlags(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt accepts all valid optimization flags.""" @@ -132,7 +132,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5, +class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by -O.""" @@ -190,7 +190,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, +class TestSizeOptimizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by -Os.""" @@ -237,7 +237,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestLegalizationPasses(expect.ValidObjectFile1_5, +class TestLegalizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl. """ diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 311cfa77..634a842e 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -4579,6 +4579,51 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); } +TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPre1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %target %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %target %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.6 or later, True Label and False Label " + "must be different labels")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index 2b6df04d..a2109db0 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -6045,6 +6045,18 @@ OpMemoryModel Logical GLSL450 "declared without SPV_KHR_non_semantic_info")); } +TEST_F(ValidateClspvReflection, DoesNotRequiresNonSemanticExtensionPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + TEST_F(ValidateClspvReflection, MissingVersion) { const std::string text = R"( OpCapability Shader diff --git a/test/val/val_extension_spv_khr_terminate_invocation.cpp b/test/val/val_extension_spv_khr_terminate_invocation.cpp index 4cabf9e2..8d924149 100644 --- a/test/val/val_extension_spv_khr_terminate_invocation.cpp +++ b/test/val/val_extension_spv_khr_terminate_invocation.cpp @@ -55,7 +55,7 @@ TEST_F(ValidateSpvKHRTerminateInvocation, Valid) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) { +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtensionPre1p6) { const std::string str = R"( OpCapability Shader OpMemoryModel Logical Simple @@ -72,9 +72,30 @@ TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) { )"; CompileSuccessfully(str.c_str()); EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("TerminateInvocation requires one of the following " - "extensions: SPV_KHR_terminate_invocation")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "TerminateInvocation requires SPIR-V version 1.6 at minimum or one " + "of the following extensions: SPV_KHR_terminate_invocation")); +} + +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresNoExtensionPost1p6) { + const std::string str = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpTerminateInvocation + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); } TEST_F(ValidateSpvKHRTerminateInvocation, RequiresShaderCapability) { diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 11b14fb0..cb2f4ef3 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -6099,6 +6099,24 @@ OpFunctionEnd "execution mode for GLCompute execution model")); } +TEST_F(ValidateImage, TypeSampledImageNotBufferPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampledBuffer +OpMemoryModel Logical GLSL450 +%float = OpTypeFloat 32 +%image = OpTypeImage %float Buffer 0 0 0 1 Unknown +%sampled = OpTypeSampledImage %image +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.6 or later, sampled image dimension must " + "not be Buffer")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_non_semantic_test.cpp b/test/val/val_non_semantic_test.cpp index b80bb1ac..210cd1ac 100644 --- a/test/val/val_non_semantic_test.cpp +++ b/test/val/val_non_semantic_test.cpp @@ -105,7 +105,7 @@ INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated, Values(""), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - MissingOpExtension, ValidateNonSemanticGenerated, + MissingOpExtensionPre1p6, ValidateNonSemanticGenerated, Combine(Values(false), Values(true), Values(""), Values(""), Values(TestResult( SPV_ERROR_INVALID_DATA, @@ -190,6 +190,26 @@ OpEntryPoint Vertex %main "main" HasSubstr("ID 2[%2] has not been defined")); } +TEST_F(ValidateNonSemanticString, MissingOpExtensionPost1p6) { + const std::string spirv = R"( +OpCapability Shader +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%test = OpExtInst %void %extinst 3 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_version_test.cpp b/test/val/val_version_test.cpp index 98565ddb..a7cb0bad 100644 --- a/test/val/val_version_test.cpp +++ b/test/val/val_version_test.cpp @@ -74,6 +74,11 @@ std::string version(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: return "1.4"; + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return "1.5"; + case SPV_ENV_UNIVERSAL_1_6: + return "1.6"; default: return "0"; } @@ -103,8 +108,13 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4,vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), @@ -115,8 +125,13 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -127,8 +142,13 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -139,13 +159,69 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), - std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); @@ -156,27 +232,69 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true), - std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), - std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true) + + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); @@ -187,6 +305,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -199,6 +320,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -211,6 +335,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_0, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_1, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -223,6 +350,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), diff --git a/tools/as/as.cpp b/tools/as/as.cpp index c8a44456..506b0585 100644 --- a/tools/as/as.cpp +++ b/tools/as/as.cpp @@ -48,7 +48,7 @@ Options: argv0, argv0, target_env_list.c_str()); } -static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, char** argv) { const char* inFile = nullptr; diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp index 6a5faa18..5380c21e 100644 --- a/tools/cfg/cfg.cpp +++ b/tools/cfg/cfg.cpp @@ -44,7 +44,7 @@ Options: argv0, argv0); } -static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, char** argv) { const char* inFile = nullptr; diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 359e8030..5c4223ed 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp @@ -25,7 +25,7 @@ namespace { -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; void print_usage(const char* program) { std::string target_env_list = spvTargetEnvList(16, 80); diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp index 5c2a82ac..d37df830 100644 --- a/tools/lint/lint.cpp +++ b/tools/lint/lint.cpp @@ -19,7 +19,7 @@ #include "tools/io.h" #include "tools/util/cli_consumer.h" -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; namespace { // Status and actions to perform after parsing command-line arguments. diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index f60d1104..3dff43f6 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -59,7 +59,7 @@ std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) { return ss.str(); } -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; std::string GetLegalizationPasses() { spvtools::Optimizer optimizer(kDefaultEnvironment); diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp index 4447b356..37600543 100644 --- a/tools/reduce/reduce.cpp +++ b/tools/reduce/reduce.cpp @@ -262,7 +262,7 @@ void DumpShader(spvtools::opt::IRContext* context, const char* filename) { DumpShader(binary, filename); } -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, const char** argv) { std::string in_binary_file; diff --git a/tools/val/val.cpp b/tools/val/val.cpp index 55321dab..d03262bb 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp @@ -77,7 +77,7 @@ Options: int main(int argc, char** argv) { const char* inFile = nullptr; - spv_target_env target_env = SPV_ENV_UNIVERSAL_1_5; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; spvtools::ValidatorOptions options; bool continue_processing = true; int return_code = 0; @@ -111,17 +111,19 @@ int main(int argc, char** argv) { printf("%s\n", spvSoftwareVersionDetailsString()); printf( "Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n " - "%s\n", + "%s\n %s\n %s\n", spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_5), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_6), spvTargetEnvDescription(SPV_ENV_OPENCL_2_2), spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), spvTargetEnvDescription(SPV_ENV_VULKAN_1_1), - spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_2)); continue_processing = false; return_code = 0; } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { -- cgit v1.2.3 From 5d0e3240f0af39195aa84a7b1d8847cad73b9f04 Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 16 Dec 2021 15:25:23 -0500 Subject: Patch location validation VUIDs (#4664) * spirv-val: Add Vulkan Location VUID * Simplify: interface checking already does all the work Co-authored-by: sfricke_samsung --- source/val/validate_decorations.cpp | 2 +- source/val/validate_interfaces.cpp | 10 +- source/val/validation_state.cpp | 19 ++ test/val/val_decoration_test.cpp | 346 ++++++++++++++++++++++++++++++++++++ test/val/val_image_test.cpp | 9 +- test/val/val_interfaces_test.cpp | 4 + test/val/val_memory_test.cpp | 4 +- 7 files changed, 385 insertions(+), 9 deletions(-) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 50c0db93..751df928 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -704,7 +704,7 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { if (d.dec_type() == SpvDecorationLocation || d.dec_type() == SpvDecorationComponent) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "A BuiltIn variable (id " << var_id + << vstate.VkErrorID(4915) << "A BuiltIn variable (id " << var_id << ") cannot have any Location or Component decorations"; } } diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index 7ccb6371..adf2e472 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -154,7 +154,7 @@ spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type, // Members cannot have location decorations at this point. if (_.HasDecoration(type->id(), SpvDecorationLocation)) { return _.diag(SPV_ERROR_INVALID_DATA, type) - << "Members cannot be assigned a location"; + << _.VkErrorID(4918) << "Members cannot be assigned a location"; } // Structs consume locations equal to the sum of the locations consumed @@ -326,8 +326,9 @@ spv_result_t GetLocationsForVariable( // Only block-decorated structs don't need a location on the variable. const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock); if (!has_location && !is_block) { + const auto vuid = (type->opcode() == SpvOpTypeStruct) ? 4917 : 4916; return _.diag(SPV_ERROR_INVALID_DATA, variable) - << "Variable must be decorated with a location"; + << _.VkErrorID(vuid) << "Variable must be decorated with a location"; } const std::string storage_class = is_output ? "output" : "input"; @@ -411,7 +412,7 @@ spv_result_t GetLocationsForVariable( auto where = member_locations.find(i - 1); if (where == member_locations.end()) { return _.diag(SPV_ERROR_INVALID_DATA, type) - << "Member index " << i - 1 + << _.VkErrorID(4919) << "Member index " << i - 1 << " is missing a location assignment"; } @@ -476,6 +477,9 @@ spv_result_t ValidateLocations(ValidationState_t& _, const Instruction* entry_point) { // According to Vulkan 14.1 only the following execution models have // locations assigned. + // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables, + // in other shader stages. Similarly, the *provisional* version of + // SPV_KHR_ray_tracing did as well, but not the final version. switch (entry_point->GetOperandAs(0)) { case SpvExecutionModelVertex: case SpvExecutionModelTessellationControl: diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index f127654e..c2282611 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -399,6 +399,15 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { features_.variable_pointers_storage_buffer = true; break; default: + // TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses + // capability SpvCapabilityRayTracingNV. + // SpvCapabilityRayTracingProvisionalKHR would need the same treatment. + // One of the differences going from SPV_KHR_ray_tracing from + // provisional to final spec was the provisional spec uses Locations + // for variables in certain storage classes, just like the + // SPV_NV_ray_tracing extension. So it mimics the NVIDIA extension. + // The final SPV_KHR_ray_tracing uses a different capability token + // number, so it doesn't fall into this case. break; } } @@ -1875,6 +1884,16 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733); case 4780: return VUID_WRAP(VUID-StandaloneSpirv-Result-04780); + case 4915: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04915); + case 4916: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04916); + case 4917: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04917); + case 4918: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04918); + case 4919: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04919); default: return ""; // unknown id } diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index f2953edc..947b985c 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -559,6 +559,8 @@ OpFunctionEnd CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04915")); EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); @@ -593,11 +595,355 @@ OpFunctionEnd CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04915")); EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); } +TEST_F(ValidateDecorations, LocationDecorationOnNumericTypeBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpDecorate %v4float Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration on target '3[%v4float]' must " + "be a variable")); +} + +TEST_F(ValidateDecorations, LocationDecorationOnStructBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpDecorate %struct Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration on target '3[%_struct_3]' " + "must be a variable")); +} + +TEST_F(ValidateDecorations, + LocationDecorationUnusedNonInterfaceVariableVulkan_Ignored) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %non_interface Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateDecorations, + LocationDecorationNonInterfaceStructVulkan_Ignored) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpMemberDecorate %block 0 Location 2 + OpMemberDecorate %block 0 Component 1 + OpDecorate %block Block + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec3 = OpTypeVector %float 3 +%outvar_ptr = OpTypePointer Output %vec3 + %fragCoord = OpVariable %outvar_ptr Output + %block = OpTypeStruct %vec3 + %invar_ptr = OpTypePointer Input %block +%non_interface = OpVariable %invar_ptr Input + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateDecorations, LocationDecorationNonInterfaceStructVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %interface + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpMemberDecorate %block 0 Location 2 + OpMemberDecorate %block 0 Component 1 + OpDecorate %block Block + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec3 = OpTypeVector %float 3 +%outvar_ptr = OpTypePointer Output %vec3 + %fragCoord = OpVariable %outvar_ptr Output + %block = OpTypeStruct %vec3 + %invar_ptr = OpTypePointer Input %block + %interface = OpVariable %invar_ptr Input ;; this variable is unused. Ignore it + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableNonStructVulkanBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %nonblock_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%nonblock_var = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04916")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable must be decorated with a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableStructNoBlockVulkanBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04917")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable must be decorated with a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableNoBlockVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block_var Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableExtraMemeberVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpDecorate %block_var Location 1 + OpMemberDecorate %block 0 Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Members cannot be assigned a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableMissingMemeberVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpMemberDecorate %block 0 Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04919")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Member index 1 is missing a location assignment")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableOnlyMemeberVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpMemberDecorate %block 0 Location 1 + OpMemberDecorate %block 1 Location 4 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + // #version 440 // #extension GL_EXT_nonuniform_qualifier : enable // layout(binding = 1) uniform sampler2D s2d[]; diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index cb2f4ef3..bf71c398 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -61,8 +61,11 @@ OpCapability ImageBuffer // In 1.4, the entry point must list all module-scope variables used. Just // list all of them. - std::string interface_vars = (env != SPV_ENV_UNIVERSAL_1_4) ? "" : - R"( + // + // For Vulkan, anything Location decoration needs to be an interface variable + std::string interface_vars = + (env != SPV_ENV_UNIVERSAL_1_4) ? "%input_flat_u32" : + R"( %uniform_image_f32_1d_0001 %uniform_image_f32_1d_0002_rgba32f %uniform_image_f32_2d_0001 @@ -1059,7 +1062,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 137[%137] cannot be a " "type")); } diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index a01fc19b..cbe48ef5 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -447,6 +447,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Members cannot be assigned a location")); } @@ -475,6 +477,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Members cannot be assigned a location")); } diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 2a884c4d..8ff40e14 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -4048,7 +4048,7 @@ TEST_F(ValidateMemory, VulkanInvariantOutputSuccess) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %main "main" +OpEntryPoint Vertex %main "main" %var OpDecorate %var Location 0 OpDecorate %var Invariant %void = OpTypeVoid @@ -4070,7 +4070,7 @@ TEST_F(ValidateMemory, VulkanInvariantInputStructSuccess) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" +OpEntryPoint Fragment %main "main" %var OpExecutionMode %main OriginUpperLeft OpDecorate %var Location 0 OpMemberDecorate %struct 1 Invariant -- cgit v1.2.3 From df2aad68b98279412494a6d449bd71b6756e699b Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 16 Dec 2021 16:48:12 -0500 Subject: val: interface struct with builtins must be Block (#4665) For a shader input/output interface variable of structure type: If the structure has BuiltIn members, then the structure type must be Block decorated. Otherwise, the variable, or the struct members must have locations, but nothing can have both a location be a BuiltIn. Implements validation needed to reject the example in https://github.com/KhronosGroup/SPIRV-Registry/issues/134 --- source/val/validate_decorations.cpp | 31 +++++++++++++++++++++++++------ test/val/val_builtins_test.cpp | 17 +++++++++++++++-- test/val/val_decoration_test.cpp | 7 +++++++ test/val/val_interfaces_test.cpp | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 8 deletions(-) diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 751df928..eb610902 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -98,6 +98,14 @@ bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) { }); } +// Returns true if the given structure type has a Block decoration. +bool isBlock(uint32_t struct_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(struct_id); + return std::any_of( + decorations.begin(), decorations.end(), + [](const Decoration& d) { return SpvDecorationBlock == d.dec_type(); }); +} + // Returns true if the given ID has the Import LinkageAttributes decoration. bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) { const auto& decorations = vstate.id_decorations(id); @@ -716,8 +724,8 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { for (uint32_t entry_point : vstate.entry_points()) { const auto& descs = vstate.entry_point_descriptions(entry_point); - int num_builtin_inputs = 0; - int num_builtin_outputs = 0; + int num_builtin_block_inputs = 0; + int num_builtin_block_outputs = 0; int num_workgroup_variables = 0; int num_workgroup_variables_with_block = 0; int num_workgroup_variables_with_aliased = 0; @@ -768,9 +776,20 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { Instruction* type_instr = vstate.FindDef(type_id); if (type_instr && SpvOpTypeStruct == type_instr->opcode() && isBuiltInStruct(type_id, vstate)) { - if (storage_class == SpvStorageClassInput) ++num_builtin_inputs; - if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs; - if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break; + if (!isBlock(type_id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_DATA, vstate.FindDef(type_id)) + << vstate.VkErrorID(4919) + << "Interface struct has no Block decoration but has " + "BuiltIn members. " + "Location decorations must be used on each member of " + "OpVariable with a structure type that is a block not " + "decorated with Location."; + } + if (storage_class == SpvStorageClassInput) ++num_builtin_block_inputs; + if (storage_class == SpvStorageClassOutput) + ++num_builtin_block_outputs; + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) + break; if (auto error = CheckBuiltInVariable(interface, vstate)) return error; } else if (isBuiltInVar(interface, vstate)) { @@ -788,7 +807,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { } } } - if (num_builtin_inputs > 1 || num_builtin_outputs > 1) { + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) { return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "There must be at most one object per Storage Class that can " diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index dff9adfe..d749c5a8 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -93,7 +93,8 @@ CodeGenerator GetInMainCodeGenerator(const char* const built_in, generator.extensions_ += extensions; } - generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn "; + generator.before_types_ = R"(OpDecorate %built_in_type Block + OpMemberDecorate %built_in_type 0 BuiltIn )"; generator.before_types_ += built_in; generator.before_types_ += "\n"; @@ -251,7 +252,8 @@ CodeGenerator GetInFunctionCodeGenerator(const char* const built_in, generator.extensions_ += extensions; } - generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn "; + generator.before_types_ = R"(OpDecorate %built_in_type Block + OpMemberDecorate %built_in_type 0 BuiltIn )"; generator.before_types_ += built_in; generator.before_types_ += "\n"; @@ -3098,6 +3100,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsFirstFails) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %input_type Block +OpDecorate %output_type Block OpMemberDecorate %input_type 0 BuiltIn FragCoord OpMemberDecorate %output_type 0 BuiltIn Position )"; @@ -3138,6 +3142,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsSecondFails) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %input_type Block +OpDecorate %output_type Block OpMemberDecorate %input_type 0 BuiltIn Position OpMemberDecorate %output_type 0 BuiltIn FragCoord )"; @@ -3201,6 +3207,7 @@ OpStore %position %f32vec4_0123 TEST_F(ValidateBuiltIns, FragmentPositionTwoEntryPoints) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn Position )"; @@ -3252,6 +3259,7 @@ CodeGenerator GetNoDepthReplacingGenerator() { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn FragDepth )"; @@ -3303,6 +3311,7 @@ CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator() { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn FragDepth )"; @@ -3374,6 +3383,7 @@ OpExtension "SPV_NV_ray_tracing" )"; generator.before_types_ = R"( +OpDecorate %input_type Block OpMemberDecorate %input_type 0 BuiltIn InstanceId )"; @@ -3609,6 +3619,7 @@ OpCapability GroupNonUniformBallot OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupEqMask %void = OpTypeVoid %int = OpTypeInt 32 0 @@ -3663,6 +3674,7 @@ OpCapability GroupNonUniform OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupSize %void = OpTypeVoid %int = OpTypeInt 32 0 @@ -3723,6 +3735,7 @@ OpCapability GroupNonUniform OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupId %void = OpTypeVoid %int = OpTypeInt 32 0 diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index 947b985c..6f5a2461 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -226,6 +226,7 @@ TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position @@ -243,6 +244,7 @@ TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position %float = OpTypeFloat 32 @@ -265,6 +267,7 @@ TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position @@ -305,6 +308,8 @@ TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) { OpEntryPoint Geometry %main "main" %in_1 %in_2 OpExecutionMode %main InputPoints OpExecutionMode %main OutputPoints + OpDecorate %struct_1 Block + OpDecorate %struct_2 Block OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 @@ -339,6 +344,8 @@ TEST_F(ValidateDecorations, OpEntryPoint Geometry %main "main" %in_1 %out_1 OpExecutionMode %main InputPoints OpExecutionMode %main OutputPoints + OpDecorate %struct_1 Block + OpDecorate %struct_2 Block OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index cbe48ef5..bec8d026 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -1500,6 +1500,42 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateInterfacesTest, StructWithBuiltinsMissingBlock_Bad) { + // See https://github.com/KhronosGroup/SPIRV-Registry/issues/134 + // + // When a shader input or output is a struct that does not have Block, + // then it must have a Location. + // But BuiltIns must not have locations. + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +; %struct needs a Block decoration +OpMemberDecorate %struct 0 BuiltIn Position +%void = OpTypeVoid +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%struct = OpTypeStruct %v4float +%in_ptr = OpTypePointer Input %struct +%in = OpVariable %in_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04919")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface struct has no Block decoration but has BuiltIn members.")); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 75e53b9f685830ac42242cf0c46cc9af523bd0df Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 4 Jan 2022 17:33:33 +0000 Subject: Avoid uninitialized access to instruction opcode (#4673) Ensures that instruction's opcode is set to something default when parsing the module with --preserve-numeric-ids enabled. This avoids uninitialized accesses and knock-on buffer overflows. Fixes #4672. --- source/text.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/text.cpp b/source/text.cpp index 88a8e8ff..415c059d 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -715,6 +715,12 @@ spv_result_t GetNumericIds(const spvtools::AssemblyGrammar& grammar, while (context.hasText()) { spv_instruction_t inst; + // Operand parsing sometimes involves knowing the opcode of the instruction + // being parsed. A malformed input might feature such an operand *before* + // the opcode is known. To guard against accessing an uninitialized opcode, + // the instruction's opcode is initialized to a default value. + inst.opcode = SpvOpMax; + if (spvTextEncodeOpcode(grammar, &context, &inst)) { return SPV_ERROR_INVALID_TEXT; } -- cgit v1.2.3 From c5ee1bc7bd7c8aab529e22925eafe633f8807759 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Fri, 7 Jan 2022 15:03:29 +0000 Subject: Fix opt fuzzer test harness (#4670) The test harness for the opt fuzzer was failing to consider that the input might use a very large id bound, despite no id approaching this bound actually being used. This change modifies the test harness to use the module's id bound, rather than looking through the module for large ids. Fixes: oss-fuzz:42386 --- test/fuzzers/spvtools_opt_fuzzer_common.cpp | 16 ++++------------ test/fuzzers/spvtools_opt_fuzzer_common.h | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.cpp b/test/fuzzers/spvtools_opt_fuzzer_common.cpp index cf790641..49785090 100644 --- a/test/fuzzers/spvtools_opt_fuzzer_common.cpp +++ b/test/fuzzers/spvtools_opt_fuzzer_common.cpp @@ -22,7 +22,7 @@ namespace fuzzers { int OptFuzzerTestOneInput( const uint8_t* data, size_t size, - std::function register_passes) { + const std::function& register_passes) { if (size < 1) { return 0; } @@ -60,17 +60,9 @@ int OptFuzzerTestOneInput( // It was not possible to build a valid module; that's OK - skip this input. return 0; } - bool found_excessively_large_id = false; - ir_context->module()->ForEachInst( - [&found_excessively_large_id](spvtools::opt::Instruction* inst) -> void { - if (inst->result_id() && inst->result_id() > kInitialIdLimit) { - found_excessively_large_id = true; - } - }, - true); - if (found_excessively_large_id) { - // The input contains a very large id. The input is thus abandoned, to avoid - // the possibility of ending up hitting the id bound limit. + if (ir_context->module()->id_bound() >= kInitialIdLimit) { + // The input already has a very large id bound. The input is thus abandoned, + // to avoid the possibility of ending up hitting the id bound limit. return 0; } diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.h b/test/fuzzers/spvtools_opt_fuzzer_common.h index 5f2f7922..b8d4281c 100644 --- a/test/fuzzers/spvtools_opt_fuzzer_common.h +++ b/test/fuzzers/spvtools_opt_fuzzer_common.h @@ -27,7 +27,7 @@ namespace fuzzers { // Helper function capturing the common logic for the various optimizer fuzzers. int OptFuzzerTestOneInput( const uint8_t* data, size_t size, - std::function register_passes); + const std::function& register_passes); } // namespace fuzzers } // namespace spvtools -- cgit v1.2.3 From b9e0e13d19326d3bbdb382232e120be2e83d1ec6 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 10 Jan 2022 14:24:44 +0000 Subject: Remove misleading comment. (#4676) The comment in `Array::GetExtraHashWords` is misleading because getting the hash words is split up between the generic `Type::GetHashWords` and the type specific `Type::GetExtraHashWords`. While `IsSameImpl` is self-contained. Removing the comment since it is misleading and no comment is really needed. Fixes #3248 --- source/opt/types.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/opt/types.cpp b/source/opt/types.cpp index b1eb3a50..ea4baadb 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp @@ -425,7 +425,6 @@ std::string Array::str() const { void Array::GetExtraHashWords(std::vector* words, std::unordered_set* seen) const { element_type_->GetHashWords(words, seen); - // This should mirror the logic in IsSameImpl words->insert(words->end(), length_info_.words.begin(), length_info_.words.end()); } -- cgit v1.2.3 From 05c839ca018b7aa2f9278ef83f86be972f655e9b Mon Sep 17 00:00:00 2001 From: Pierre Moreau Date: Mon, 10 Jan 2022 16:55:47 +0100 Subject: Improvements to disassembly within PassManager (#4677) * PassManager: Print errors occurring during disassembly Otherwise one could be greeted by the following text when running spirv-opt withe the `--print-all` flag: ; IR before pass wrap-opkill ; IR before pass eliminate-dead-branches ; IR before pass merge-return With this commit, one will instead get: error: line 143: Invalid opcode: 400 warning: line 0: Disassembly failed before pass wrap-opkill error: line 143: Invalid opcode: 400 warning: line 0: Disassembly failed before pass eliminate-dead-branches error: line 143: Invalid opcode: 400 warning: line 0: Disassembly failed before pass merge-return * PassManager: Use the right target environment when disassembling Disassembly would fail if features from a newer version of SPIR-V than 1.2 were used. --- source/opt/pass_manager.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/opt/pass_manager.cpp b/source/opt/pass_manager.cpp index be53d344..a73ff7cf 100644 --- a/source/opt/pass_manager.cpp +++ b/source/opt/pass_manager.cpp @@ -35,10 +35,18 @@ Pass::Status PassManager::Run(IRContext* context) { if (print_all_stream_) { std::vector binary; context->module()->ToBinary(&binary, false); - SpirvTools t(SPV_ENV_UNIVERSAL_1_2); + SpirvTools t(target_env_); + t.SetMessageConsumer(consumer()); std::string disassembly; - t.Disassemble(binary, &disassembly, 0); - *print_all_stream_ << preamble << (pass ? pass->name() : "") << "\n" + std::string pass_name = (pass ? pass->name() : ""); + if (!t.Disassemble(binary, &disassembly, 0)) { + std::string msg = "Disassembly failed before pass "; + msg += pass_name + "\n"; + spv_position_t null_pos{0, 0, 0}; + consumer()(SPV_MSG_WARNING, "", null_pos, msg.c_str()); + return; + } + *print_all_stream_ << preamble << pass_name << "\n" << disassembly << std::endl; } }; -- cgit v1.2.3 From 8fda47ca08cff2494e068745aa0619280113baf7 Mon Sep 17 00:00:00 2001 From: Minmin Gong Date: Tue, 11 Jan 2022 08:11:06 -0800 Subject: Disable a codepage warning on non-English OS (#4668) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70caf857..76b87d8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ if(${COMPILER_IS_LIKE_GNU}) set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror) endif() elseif(MSVC) - set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800) + set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819) if(${SPIRV_WERROR}) set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX) -- cgit v1.2.3 From 42dc678913cc6641927ee695bdb400c8d6d0cbb6 Mon Sep 17 00:00:00 2001 From: Pierre Moreau Date: Thu, 13 Jan 2022 01:04:13 +0100 Subject: Remove duplicated "the" from comments (#4666) --- source/binary.cpp | 2 +- source/cfa.h | 2 +- source/fuzz/fuzzer_context.cpp | 2 +- source/opt/dead_branch_elim_pass.h | 2 +- source/opt/desc_sroa.h | 2 +- source/opt/dominator_tree.h | 2 +- source/opt/loop_fission.cpp | 2 +- source/opt/register_pressure.cpp | 2 +- source/opt/scalar_analysis_simplification.cpp | 2 +- source/opt/strength_reduction_pass.h | 2 +- source/val/validate.cpp | 2 +- source/val/validate_memory.cpp | 2 +- utils/vscode/src/lsp/protocol/tsprotocol.go | 6 +++--- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/source/binary.cpp b/source/binary.cpp index 48a94f1e..bbaa2c2c 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -300,7 +300,7 @@ spv_result_t Parser::parseInstruction() { const uint32_t first_word = peek(); // If the module's endianness is different from the host native endianness, - // then converted_words contains the the endian-translated words in the + // then converted_words contains the endian-translated words in the // instruction. _.endian_converted_words.clear(); _.endian_converted_words.push_back(first_word); diff --git a/source/cfa.h b/source/cfa.h index 0d09014c..7cadf55f 100644 --- a/source/cfa.h +++ b/source/cfa.h @@ -42,7 +42,7 @@ class CFA { /// Returns true if a block with @p id is found in the @p work_list vector /// - /// @param[in] work_list Set of blocks visited in the the depth first + /// @param[in] work_list Set of blocks visited in the depth first /// traversal /// of the CFG /// @param[in] id The ID of the block being checked diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index c2175425..7e34cc33 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -21,7 +21,7 @@ namespace fuzz { namespace { -// An offset between the the module's id bound and the minimum fresh id. +// An offset between the module's id bound and the minimum fresh id. // // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541): consider // the case where the maximum id bound is reached. diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h index 7841bc47..c350bb2f 100644 --- a/source/opt/dead_branch_elim_pass.h +++ b/source/opt/dead_branch_elim_pass.h @@ -98,7 +98,7 @@ class DeadBranchElimPass : public MemPass { // Fix phis in reachable blocks so that only live (or unremovable) incoming // edges are present. If the block now only has a single live incoming edge, // remove the phi and replace its uses with its data input. If the single - // remaining incoming edge is from the phi itself, the the phi is in an + // remaining incoming edge is from the phi itself, the phi is in an // unreachable single block loop. Either the block is dead and will be // removed, or it's reachable from an unreachable continue target. In the // latter case that continue target block will be collapsed into a block that diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index 383d441c..6a24fd87 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h @@ -132,7 +132,7 @@ class DescriptorScalarReplacement : public Pass { // A map from an OpVariable instruction to the set of variables that will be // used to replace it. The entry |replacement_variables_[var][i]| is the id of - // a variable that will be used in the place of the the ith element of the + // a variable that will be used in the place of the ith element of the // array |var|. If the entry is |0|, then the variable has not been // created yet. std::map> replacement_variables_; diff --git a/source/opt/dominator_tree.h b/source/opt/dominator_tree.h index 0024bc50..1674b228 100644 --- a/source/opt/dominator_tree.h +++ b/source/opt/dominator_tree.h @@ -278,7 +278,7 @@ class DominatorTree { private: // Wrapper function which gets the list of pairs of each BasicBlocks to its - // immediately dominating BasicBlock and stores the result in the the edges + // immediately dominating BasicBlock and stores the result in the edges // parameter. // // The |edges| vector will contain the dominator tree as pairs of nodes. diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp index 0678113c..0a4125df 100644 --- a/source/opt/loop_fission.cpp +++ b/source/opt/loop_fission.cpp @@ -453,7 +453,7 @@ Pass::Status LoopFissionPass::Process() { for (Function& f : *context()->module()) { // We collect all the inner most loops in the function and run the loop // splitting util on each. The reason we do this is to allow us to iterate - // over each, as creating new loops will invalidate the the loop iterator. + // over each, as creating new loops will invalidate the loop iterator. std::vector inner_most_loops{}; LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); for (Loop& loop : loop_descriptor) { diff --git a/source/opt/register_pressure.cpp b/source/opt/register_pressure.cpp index 5750c6d4..1ad33738 100644 --- a/source/opt/register_pressure.cpp +++ b/source/opt/register_pressure.cpp @@ -378,7 +378,7 @@ void RegisterLiveness::SimulateFusion( // The loop fusion is injecting the l1 before the l2, the latch of l1 will be // connected to the header of l2. // To compute the register usage, we inject the loop live-in (union of l1 and - // l2 live-in header blocks) into the the live in/out of each basic block of + // l2 live-in header blocks) into the live in/out of each basic block of // l1 to get the peak register usage. We then repeat the operation to for l2 // basic blocks but in this case we inject the live-out of the latch of l1. auto live_loop = MakeFilterIteratorRange( diff --git a/source/opt/scalar_analysis_simplification.cpp b/source/opt/scalar_analysis_simplification.cpp index 52f2d6ad..9c81dbe9 100644 --- a/source/opt/scalar_analysis_simplification.cpp +++ b/source/opt/scalar_analysis_simplification.cpp @@ -134,7 +134,7 @@ class SENodeSimplifyImpl { // offset. SENode* EliminateZeroCoefficientRecurrents(SENode* node); - // A reference the the analysis which requested the simplification. + // A reference the analysis which requested the simplification. ScalarEvolutionAnalysis& analysis_; // The node being simplified. diff --git a/source/opt/strength_reduction_pass.h b/source/opt/strength_reduction_pass.h index 8dfeb307..1cbbbcc6 100644 --- a/source/opt/strength_reduction_pass.h +++ b/source/opt/strength_reduction_pass.h @@ -34,7 +34,7 @@ class StrengthReductionPass : public Pass { // Returns true if something changed. bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*); - // Scan the types and constants in the module looking for the the integer + // Scan the types and constants in the module looking for the integer // types that we are // interested in. The shift operation needs a small unsigned integer. We // need to find diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 7655c960..64df67ca 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -202,7 +202,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( /* diagnostic = */ nullptr); // Parse the module and perform inline validation checks. These checks do - // not require the the knowledge of the whole module. + // not require the knowledge of the whole module. if (auto error = spvBinaryParse(&context, vstate, words, num_words, /*parsed_header =*/nullptr, ProcessInstruction, pDiagnostic)) { diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index a7b0f82d..93b18000 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -1411,7 +1411,7 @@ spv_result_t ValidateArrayLength(ValidationState_t& state, << state.getIdName(inst->id()) << "' must be an OpTypeRuntimeArray."; } - // The array member must the the index of the last element (the run time + // The array member must the index of the last element (the run time // array). if (inst->GetOperandAs(3) != num_of_members - 1) { return state.diag(SPV_ERROR_INVALID_ID, inst) diff --git a/utils/vscode/src/lsp/protocol/tsprotocol.go b/utils/vscode/src/lsp/protocol/tsprotocol.go index 50543fc8..29dd9867 100644 --- a/utils/vscode/src/lsp/protocol/tsprotocol.go +++ b/utils/vscode/src/lsp/protocol/tsprotocol.go @@ -2844,7 +2844,7 @@ type LocationLink struct { /*TargetSelectionRange defined: * The range that should be selected and revealed when this link is being followed, e.g the name of a function. - * Must be contained by the the `targetRange`. See also `DocumentSymbol#range` + * Must be contained by the `targetRange`. See also `DocumentSymbol#range` */ TargetSelectionRange Range `json:"targetSelectionRange"` } @@ -3627,14 +3627,14 @@ type DocumentSymbol struct { /*Range defined: * The range enclosing this symbol not including leading/trailing whitespace but everything else - * like comments. This information is typically used to determine if the the clients cursor is + * like comments. This information is typically used to determine if the clients cursor is * inside the symbol to reveal in the symbol in the UI. */ Range Range `json:"range"` /*SelectionRange defined: * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. - * Must be contained by the the `range`. + * Must be contained by the `range`. */ SelectionRange Range `json:"selectionRange"` -- cgit v1.2.3 From 8a40f6de57d7b78bc431678d90aa8a570d1631f2 Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 13 Jan 2022 13:49:35 -0500 Subject: Add SPIR-V 1.6 support to wasm build (#4674) * Add SPIR-V 1.6 support to wasm build * Fix formatting --- source/wasm/spirv-tools.cpp | 5 +++-- source/wasm/spirv-tools.d.ts | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/wasm/spirv-tools.cpp b/source/wasm/spirv-tools.cpp index 90407f32..33f2f05f 100644 --- a/source/wasm/spirv-tools.cpp +++ b/source/wasm/spirv-tools.cpp @@ -78,7 +78,8 @@ EMSCRIPTEN_BINDINGS(my_module) { constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); constant("SPV_ENV_UNIVERSAL_1_5", static_cast(SPV_ENV_UNIVERSAL_1_5)); constant("SPV_ENV_VULKAN_1_2", static_cast(SPV_ENV_VULKAN_1_2)); - + constant("SPV_ENV_UNIVERSAL_1_6", + static_cast(SPV_ENV_UNIVERSAL_1_6)); constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast(SPV_BINARY_TO_TEXT_OPTION_NONE)); constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast(SPV_BINARY_TO_TEXT_OPTION_PRINT)); @@ -90,4 +91,4 @@ EMSCRIPTEN_BINDINGS(my_module) { constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast(SPV_TEXT_TO_BINARY_OPTION_NONE)); constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)); -} \ No newline at end of file +} diff --git a/source/wasm/spirv-tools.d.ts b/source/wasm/spirv-tools.d.ts index 9c197973..c06bdf18 100644 --- a/source/wasm/spirv-tools.d.ts +++ b/source/wasm/spirv-tools.d.ts @@ -40,6 +40,7 @@ declare interface SpirvTools { SPV_ENV_VULKAN_1_1_SPIRV_1_4: number; SPV_ENV_UNIVERSAL_1_5: number; SPV_ENV_VULKAN_1_2: number; + SPV_ENV_UNIVERSAL_1_6: number; SPV_TEXT_TO_BINARY_OPTION_NONE: number; SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS: number; -- cgit v1.2.3 From c38495656dca7cb9c8f50a17b334537fa1441d7c Mon Sep 17 00:00:00 2001 From: Pierre Moreau Date: Sat, 22 Jan 2022 21:40:19 +0100 Subject: Linker improvements (#4679) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test/linker: Code factorisation and small tweaks * linker: Do not fail when going over limits The limits are minima and implementations or APIs might support higher limits, so just warn the user about it. And only check for the limits right before emitting the binary, as limits might change earlier when removing duplicate instructions, function prototypes, etc. The only check performed right before merging, is making sure the ID bound will not overflow the 32 bits following the merge. Also, use the defines for the limits instead of hard-coding them. * linker: Require a memory model in each input module The existing code could run into weird situation. For example, if the first module had no memory model, it would not emit any memory model (sort of reasonable) and would accept without complains all possible mix from later modules as it would not verify them. * linker: Replace hex version with SPV_SPIRV_VERSION_WORD * linker: Error out when linking together different versions Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4135 * tools/linker: Do not write to disk if linking failed Also, do not consider warnings as errors. * tools/linker: Fix formatting in help message * tools/linker: Further clarify the use of --target-env Also update the text for the default version to reflect the change made in 7d768812 ("Basic support for SPIR-V 1.6 (#4663)"). --- source/link/linker.cpp | 173 ++++++++++++++-------- test/link/binary_version_test.cpp | 67 +++++---- test/link/entry_points_test.cpp | 18 ++- test/link/global_values_amount_test.cpp | 192 +++++++++++-------------- test/link/ids_limit_test.cpp | 133 ++++++++++++----- test/link/matching_imports_to_exports_test.cpp | 139 ++++++++++++++---- test/link/memory_model_test.cpp | 39 ++++- test/link/partial_linkage_test.cpp | 18 ++- test/link/type_match_test.cpp | 1 + test/link/unique_ids_test.cpp | 3 +- tools/link/linker.cpp | 16 ++- 11 files changed, 531 insertions(+), 268 deletions(-) diff --git a/source/link/linker.cpp b/source/link/linker.cpp index 21cada70..cedc1386 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -87,10 +87,6 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, // // |header| should not be null, |modules| should not be empty and pointers // should be non-null. |max_id_bound| should be strictly greater than 0. -// -// TODO(pierremoreau): What to do when binaries use different versions of -// SPIR-V? For now, use the max of all versions found in -// the input modules. spv_result_t GenerateHeader(const MessageConsumer& consumer, const std::vector& modules, uint32_t max_id_bound, opt::ModuleHeader* header); @@ -149,6 +145,15 @@ spv_result_t RemoveLinkageSpecificInstructions( spv_result_t VerifyIds(const MessageConsumer& consumer, opt::IRContext* linked_context); +// Verify that the universal limits are not crossed, and warn the user +// otherwise. +// +// TODO(pierremoreau): +// - Verify against the limits of the environment (e.g. Vulkan limits if +// consuming vulkan1.x) +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context); + spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, std::vector* modules, uint32_t* max_id_bound) { @@ -164,7 +169,7 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of ShiftIdsInModules should not be null."; - uint32_t id_bound = modules->front()->IdBound() - 1u; + size_t id_bound = modules->front()->IdBound() - 1u; for (auto module_iter = modules->begin() + 1; module_iter != modules->end(); ++module_iter) { Module* module = *module_iter; @@ -172,21 +177,18 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, insn->ForEachId([&id_bound](uint32_t* id) { *id += id_bound; }); }); id_bound += module->IdBound() - 1u; - if (id_bound > 0x3FFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) - << "The limit of IDs, 4194303, was exceeded:" - << " " << id_bound << " is the current ID bound."; // Invalidate the DefUseManager module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); } ++id_bound; - if (id_bound > 0x3FFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) - << "The limit of IDs, 4194303, was exceeded:" - << " " << id_bound << " is the current ID bound."; + if (id_bound > std::numeric_limits::max()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "Too many IDs (" << id_bound + << "): combining all modules would overflow the 32-bit word of the " + "SPIR-V header."; - *max_id_bound = id_bound; + *max_id_bound = static_cast(id_bound); return SPV_SUCCESS; } @@ -203,12 +205,22 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer, return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of GenerateHeader should not be null."; - uint32_t version = 0u; - for (const auto& module : modules) - version = std::max(version, module->version()); + const uint32_t linked_version = modules.front()->version(); + for (std::size_t i = 1; i < modules.size(); ++i) { + const uint32_t module_version = modules[i]->version(); + if (module_version != linked_version) + return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting SPIR-V versions: " + << SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(linked_version) + << " (input modules 1 through " << i << ") vs " + << SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(module_version) + << " (input module " << (i + 1) << ")."; + } header->magic_number = SpvMagicNumber; - header->version = version; + header->version = linked_version; header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0); header->bound = max_id_bound; header->schema = 0u; @@ -244,44 +256,55 @@ spv_result_t MergeModules(const MessageConsumer& consumer, linked_module->AddExtInstImport( std::unique_ptr(inst.Clone(linked_context))); - do { - const Instruction* memory_model_inst = input_modules[0]->GetMemoryModel(); - if (memory_model_inst == nullptr) break; - - uint32_t addressing_model = memory_model_inst->GetSingleWordOperand(0u); - uint32_t memory_model = memory_model_inst->GetSingleWordOperand(1u); - for (const auto& module : input_modules) { - memory_model_inst = module->GetMemoryModel(); - if (memory_model_inst == nullptr) continue; - - if (addressing_model != memory_model_inst->GetSingleWordOperand(0u)) { - spv_operand_desc initial_desc = nullptr, current_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - addressing_model, &initial_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - memory_model_inst->GetSingleWordOperand(0u), - ¤t_desc); - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting addressing models: " << initial_desc->name - << " vs " << current_desc->name << "."; - } - if (memory_model != memory_model_inst->GetSingleWordOperand(1u)) { - spv_operand_desc initial_desc = nullptr, current_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, memory_model, - &initial_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, - memory_model_inst->GetSingleWordOperand(1u), - ¤t_desc); - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting memory models: " << initial_desc->name << " vs " - << current_desc->name << "."; - } + const Instruction* linked_memory_model_inst = + input_modules.front()->GetMemoryModel(); + if (linked_memory_model_inst == nullptr) { + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module 1 is lacking an OpMemoryModel instruction."; + } + const uint32_t linked_addressing_model = + linked_memory_model_inst->GetSingleWordOperand(0u); + const uint32_t linked_memory_model = + linked_memory_model_inst->GetSingleWordOperand(1u); + + for (std::size_t i = 1; i < input_modules.size(); ++i) { + const Module* module = input_modules[i]; + const Instruction* memory_model_inst = module->GetMemoryModel(); + if (memory_model_inst == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module " << (i + 1) + << " is lacking an OpMemoryModel instruction."; + + const uint32_t module_addressing_model = + memory_model_inst->GetSingleWordOperand(0u); + if (module_addressing_model != linked_addressing_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + linked_addressing_model, &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + module_addressing_model, &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting addressing models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; } - if (memory_model_inst != nullptr) - linked_module->SetMemoryModel(std::unique_ptr( - memory_model_inst->Clone(linked_context))); - } while (false); + const uint32_t module_memory_model = + memory_model_inst->GetSingleWordOperand(1u); + if (module_memory_model != linked_memory_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, linked_memory_model, + &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, module_memory_model, + &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting memory models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; + } + } + linked_module->SetMemoryModel(std::unique_ptr( + linked_memory_model_inst->Clone(linked_context))); std::vector> entry_points; for (const auto& module : input_modules) @@ -332,7 +355,7 @@ spv_result_t MergeModules(const MessageConsumer& consumer, // If the generated module uses SPIR-V 1.1 or higher, add an // OpModuleProcessed instruction about the linking step. - if (linked_module->version() >= 0x10100) { + if (linked_module->version() >= SPV_SPIRV_VERSION_WORD(1, 1)) { const std::string processed_string("Linked by SPIR-V Tools Linker"); std::vector processed_words = spvtools::utils::MakeVector(processed_string); @@ -349,18 +372,12 @@ spv_result_t MergeModules(const MessageConsumer& consumer, // TODO(pierremoreau): Since the modules have not been validate, should we // expect SpvStorageClassFunction variables outside // functions? - uint32_t num_global_values = 0u; for (const auto& module : input_modules) { for (const auto& inst : module->types_values()) { linked_module->AddType( std::unique_ptr(inst.Clone(linked_context))); - num_global_values += inst.opcode() == SpvOpVariable; } } - if (num_global_values > 0xFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "The limit of global values, 65535, was exceeded;" - << " " << num_global_values << " global values were found."; // Process functions and their basic blocks for (const auto& module : input_modules) { @@ -632,6 +649,34 @@ spv_result_t VerifyIds(const MessageConsumer& consumer, return SPV_SUCCESS; } +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context) { + spv_position_t position = {}; + + const uint32_t max_id_bound = linked_context.module()->id_bound(); + if (max_id_bound >= SPV_LIMIT_RESULT_ID_BOUND) + DiagnosticStream({0u, 0u, 4u}, consumer, "", SPV_WARNING) + << "The minimum limit of IDs, " << (SPV_LIMIT_RESULT_ID_BOUND - 1) + << ", was exceeded:" + << " " << max_id_bound << " is the current ID bound.\n" + << "The resulting module might not be supported by all " + "implementations."; + + size_t num_global_values = 0u; + for (const auto& inst : linked_context.module()->types_values()) { + num_global_values += inst.opcode() == SpvOpVariable; + } + if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX) + DiagnosticStream(position, consumer, "", SPV_WARNING) + << "The minimum limit of global values, " + << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded;" + << " " << num_global_values << " global values were found.\n" + << "The resulting module might not be supported by all " + "implementations."; + + return SPV_SUCCESS; +} + } // namespace spv_result_t Link(const Context& context, @@ -756,7 +801,11 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries, pass_res = manager.Run(&linked_context); if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; - // Phase 11: Output the module + // Phase 11: Warn if SPIR-V limits were exceeded + res = VerifyLimits(consumer, linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 12: Output the module linked_context.module()->ToBinary(linked_binary, true); return SPV_SUCCESS; diff --git a/test/link/binary_version_test.cpp b/test/link/binary_version_test.cpp index 80aab0fd..a56030f4 100644 --- a/test/link/binary_version_test.cpp +++ b/test/link/binary_version_test.cpp @@ -20,40 +20,57 @@ namespace spvtools { namespace { +using ::testing::HasSubstr; using BinaryVersion = spvtest::LinkerTest; -TEST_F(BinaryVersion, LinkerChoosesMaxSpirvVersion) { +spvtest::Binary CreateBinary(uint32_t version) { + return { + // clang-format off + // Header + SpvMagicNumber, + version, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 1u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + // OpCapability Shader + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + // OpMemoryModel Logical Simple + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple + // clang-format on + }; +} + +TEST_F(BinaryVersion, Match) { // clang-format off spvtest::Binaries binaries = { - { - SpvMagicNumber, - 0x00010300u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - }, - { - SpvMagicNumber, - 0x00010500u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - }, - { - SpvMagicNumber, - 0x00010100u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - } + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), }; // clang-format on spvtest::Binary linked_binary; - - ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_EQ(SPV_SPIRV_VERSION_WORD(1, 3), linked_binary[1]); +} - EXPECT_EQ(0x00010500u, linked_binary[1]); +TEST_F(BinaryVersion, Mismatch) { + // clang-format off + spvtest::Binaries binaries = { + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 5)), + }; + // clang-format on + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Conflicting SPIR-V versions: 1.3 (input modules 1 " + "through 1) vs 1.5 (input module 2).")); } } // namespace diff --git a/test/link/entry_points_test.cpp b/test/link/entry_points_test.cpp index bac8e02e..df7ea20c 100644 --- a/test/link/entry_points_test.cpp +++ b/test/link/entry_points_test.cpp @@ -26,6 +26,8 @@ class EntryPoints : public spvtest::LinkerTest {}; TEST_F(EntryPoints, SameModelDifferentName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -33,6 +35,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "bar" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -41,12 +45,15 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPoints, DifferentModelSameName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -54,6 +61,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -62,12 +71,15 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPoints, SameModelAndName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -75,6 +87,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 diff --git a/test/link/global_values_amount_test.cpp b/test/link/global_values_amount_test.cpp index 2c4ee1f0..3158b7ec 100644 --- a/test/link/global_values_amount_test.cpp +++ b/test/link/global_values_amount_test.cpp @@ -22,85 +22,56 @@ namespace { using ::testing::HasSubstr; +const uint32_t binary_count = 2u; + class EntryPointsAmountTest : public spvtest::LinkerTest { public: - EntryPointsAmountTest() { binaries.reserve(0xFFFF); } + EntryPointsAmountTest() { binaries.reserve(binary_count + 1u); } void SetUp() override { - binaries.push_back({SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 10u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width - - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u, // NOTE: Type ID - - 2u << SpvWordCountShift | SpvOpTypeVoid, - 3u, // NOTE: Result ID - - 3u << SpvWordCountShift | SpvOpTypeFunction, - 4u, // NOTE: Result ID - 3u, // NOTE: Return type - - 5u << SpvWordCountShift | SpvOpFunction, - 3u, // NOTE: Result type - 5u, // NOTE: Result ID - SpvFunctionControlMaskNone, - 4u, // NOTE: Function type - - 2u << SpvWordCountShift | SpvOpLabel, - 6u, // NOTE: Result ID - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 7u, // NOTE: Result ID - SpvStorageClassFunction, - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 8u, // NOTE: Result ID - SpvStorageClassFunction, - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 9u, // NOTE: Result ID - SpvStorageClassFunction, - - 1u << SpvWordCountShift | SpvOpReturn, - - 1u << SpvWordCountShift | SpvOpFunctionEnd}); - for (size_t i = 0u; i < 2u; ++i) { - spvtest::Binary binary = { - SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 103u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width - - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u // NOTE: Type ID - }; - - for (uint32_t j = 0u; j < 0xFFFFu / 2u; ++j) { - binary.push_back(4u << SpvWordCountShift | SpvOpVariable); - binary.push_back(2u); // NOTE: Type ID - binary.push_back(j + 3u); // NOTE: Result ID - binary.push_back(SpvStorageClassInput); - } - binaries.push_back(binary); + const uint32_t global_variable_count_per_binary = + (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1u) / binary_count; + + spvtest::Binary common_binary = { + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 3u + global_variable_count_per_binary, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, + + SpvOpTypeFloat | 3u << SpvWordCountShift, + 1u, // NOTE: Result ID + 32u, // NOTE: Width + + SpvOpTypePointer | 4u << SpvWordCountShift, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u // NOTE: Type ID + // clang-format on + }; + + binaries.push_back({}); + spvtest::Binary& binary = binaries.back(); + binary.reserve(common_binary.size() + global_variable_count_per_binary * 4); + binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend()); + + for (uint32_t i = 0u; i < global_variable_count_per_binary; ++i) { + binary.push_back(SpvOpVariable | 4u << SpvWordCountShift); + binary.push_back(2u); // NOTE: Type ID + binary.push_back(3u + i); // NOTE: Result ID + binary.push_back(SpvStorageClassInput); + } + + for (uint32_t i = 0u; i < binary_count - 1u; ++i) { + binaries.push_back(binaries.back()); } } void TearDown() override { binaries.clear(); } @@ -111,42 +82,53 @@ class EntryPointsAmountTest : public spvtest::LinkerTest { TEST_F(EntryPointsAmountTest, UnderLimit) { spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPointsAmountTest, OverLimit) { - binaries.push_back({SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 5u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width - - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u, // NOTE: Type ID - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 3u, // NOTE: Result ID - SpvStorageClassInput, - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 4u, // NOTE: Result ID - SpvStorageClassInput}); + binaries.push_back({ + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 5u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, + + SpvOpTypeFloat | 3u << SpvWordCountShift, + 1u, // NOTE: Result ID + 32u, // NOTE: Width + + SpvOpTypePointer | 4u << SpvWordCountShift, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u, // NOTE: Type ID + + SpvOpVariable | 4u << SpvWordCountShift, + 2u, // NOTE: Type ID + 3u, // NOTE: Result ID + SpvStorageClassInput, + + SpvOpVariable | 4u << SpvWordCountShift, + 2u, // NOTE: Type ID + 4u, // NOTE: Result ID + SpvStorageClassInput + // clang-format on + }); spvtest::Binary linked_binary; - - EXPECT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary)); - EXPECT_THAT(GetErrorMessage(), - HasSubstr("The limit of global values, 65535, was exceeded; " - "65536 global values were found.")); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("The minimum limit of global values, 65535, was exceeded; " + "65536 global values were found.")); } } // namespace diff --git a/test/link/ids_limit_test.cpp b/test/link/ids_limit_test.cpp index 6d7815a2..21c64b97 100644 --- a/test/link/ids_limit_test.cpp +++ b/test/link/ids_limit_test.cpp @@ -21,51 +21,114 @@ namespace spvtools { namespace { using ::testing::HasSubstr; -using IdsLimit = spvtest::LinkerTest; + +class IdsLimit : public spvtest::LinkerTest { + public: + IdsLimit() { binaries.reserve(2u); } + + void SetUp() override { + const uint32_t id_bound = SPV_LIMIT_RESULT_ID_BOUND - 1u; + const uint32_t constant_count = + id_bound - + 2u; // One ID is used for TypeBool, and (constant_count + 1) < id_bound + + // This is needed, as otherwise the ID bound will get reset to 1 while + // running the RemoveDuplicates pass. + spvtest::Binary common_binary = { + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + id_bound, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, + + SpvOpTypeBool | 2u << SpvWordCountShift, + 1u // NOTE: Result ID + // clang-format on + }; + + binaries.push_back({}); + spvtest::Binary& binary = binaries.back(); + binary.reserve(common_binary.size() + constant_count * 3u); + binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend()); + + for (uint32_t i = 0u; i < constant_count; ++i) { + binary.push_back(SpvOpConstantTrue | 3u << SpvWordCountShift); + binary.push_back(1u); // NOTE: Type ID + binary.push_back(2u + i); // NOTE: Result ID + } + } + void TearDown() override { binaries.clear(); } + + spvtest::Binaries binaries; +}; + +spvtest::Binary CreateBinary(uint32_t id_bound) { + return { + // clang-format off + // Header + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + id_bound, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + // OpCapability Shader + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + // OpMemoryModel Logical Simple + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple + // clang-format on + }; +} TEST_F(IdsLimit, UnderLimit) { - spvtest::Binaries binaries = { - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x2FFFFFu, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x100000u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }}; spvtest::Binary linked_binary; - - ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); - EXPECT_EQ(0x3FFFFEu, linked_binary[3]); + EXPECT_EQ(0x3FFFFFu, linked_binary[3]); } TEST_F(IdsLimit, OverLimit) { - spvtest::Binaries binaries = { - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x2FFFFFu, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x100000u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 3u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }}; + spvtest::Binary& binary = binaries.back(); + + const uint32_t id_bound = binary[3]; + binary[3] = id_bound + 1u; + + binary.push_back(SpvOpConstantFalse | 3u << SpvWordCountShift); + binary.push_back(1u); // NOTE: Type ID + binary.push_back(id_bound); // NOTE: Result ID + + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("The minimum limit of IDs, 4194303, was exceeded: 4194304 is " + "the current ID bound.")); + EXPECT_EQ(0x400000u, linked_binary[3]); +} + +TEST_F(IdsLimit, Overflow) { + spvtest::Binaries binaries = {CreateBinary(0xFFFFFFFFu), + CreateBinary(0x00000002u)}; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_ERROR_INVALID_ID, Link(binaries, &linked_binary)); - EXPECT_THAT(GetErrorMessage(), - HasSubstr("The limit of IDs, 4194303, was exceeded: 4194304 is " - "the current ID bound.")); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, Link(binaries, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Too many IDs (4294967296): combining all modules would " + "overflow the 32-bit word of the SPIR-V header.")); } } // namespace diff --git a/test/link/matching_imports_to_exports_test.cpp b/test/link/matching_imports_to_exports_test.cpp index e76c69fb..6b02fc46 100644 --- a/test/link/matching_imports_to_exports_test.cpp +++ b/test/link/matching_imports_to_exports_test.cpp @@ -26,6 +26,9 @@ using MatchingImportsToExports = spvtest::LinkerTest; TEST_F(MatchingImportsToExports, Default) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -33,6 +36,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -40,11 +46,14 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeFloat 32 %2 = OpVariable %1 Input %3 = OpConstant %1 42 @@ -52,7 +61,7 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -60,23 +69,29 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, NotALibraryExtraExports) { const std::string body = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeFloat 32 %2 = OpVariable %1 Uniform )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -84,6 +99,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, LibraryExtraExports) { const std::string body = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -92,10 +110,13 @@ OpDecorate %1 LinkageAttributes "foo" Export spvtest::Binary linked_binary; LinkerOptions options; options.SetCreateLibrary(true); - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options)) << GetErrorMessage(); const std::string expected_res = R"(OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 @@ -103,7 +124,7 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -111,11 +132,18 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, UnresolvedImports) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform )"; - const std::string body2 = R"()"; + const std::string body2 = R"( +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +)"; spvtest::Binary linked_binary; EXPECT_EQ(SPV_ERROR_INVALID_BINARY, @@ -127,6 +155,9 @@ OpDecorate %1 LinkageAttributes "foo" Import TEST_F(MatchingImportsToExports, TypeMismatch) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -134,6 +165,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeInt 32 0 %3 = OpConstant %2 42 @@ -153,6 +187,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, MultipleDefinitions) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -160,6 +197,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -167,6 +207,9 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; const std::string body3 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 -1 @@ -185,6 +228,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, SameNameDifferentTypes) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -192,6 +238,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeInt 32 0 %3 = OpConstant %2 42 @@ -199,6 +248,9 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; const std::string body3 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 12 @@ -217,6 +269,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, DecorationMismatch) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 Constant %2 = OpTypeFloat 32 @@ -225,6 +280,9 @@ OpDecorate %2 Constant )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -244,8 +302,10 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, FuncParamAttrDifferButStillMatchExportToImport) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 FuncParamAttr Zext %3 = OpTypeVoid @@ -256,8 +316,10 @@ OpDecorate %2 FuncParamAttr Zext OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export OpDecorate %2 FuncParamAttr Sext %3 = OpTypeVoid @@ -271,10 +333,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 FuncParamAttr Sext %2 = OpTypeVoid @@ -288,7 +352,7 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -296,6 +360,9 @@ OpFunctionEnd TEST_F(MatchingImportsToExports, FunctionCtrl) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeVoid %3 = OpTypeFunction %2 @@ -306,6 +373,9 @@ OpFunctionEnd )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeVoid %3 = OpTypeFunction %2 @@ -316,11 +386,14 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeVoid %2 = OpTypeFunction %1 %3 = OpTypeFloat 32 @@ -332,15 +405,17 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } TEST_F(MatchingImportsToExports, UseExportedFuncParamAttr) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 FuncParamAttr Zext %2 = OpDecorationGroup @@ -356,8 +431,10 @@ OpFunctionEnd OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export OpDecorate %2 FuncParamAttr Sext %3 = OpTypeVoid @@ -371,10 +448,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 FuncParamAttr Zext %1 = OpDecorationGroup @@ -394,15 +473,17 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } TEST_F(MatchingImportsToExports, NamesAndDecorations) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %3 "param" OpDecorate %1 LinkageAttributes "foo" Import @@ -422,8 +503,10 @@ OpFunctionEnd OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %2 "param" OpDecorate %1 LinkageAttributes "foo" Export @@ -440,10 +523,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %2 "param" OpModuleProcessed "Linked by SPIR-V Tools Linker" @@ -467,7 +552,7 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } diff --git a/test/link/memory_model_test.cpp b/test/link/memory_model_test.cpp index 2add5046..280a776a 100644 --- a/test/link/memory_model_test.cpp +++ b/test/link/memory_model_test.cpp @@ -50,9 +50,9 @@ OpMemoryModel Physical32 Simple spvtest::Binary linked_binary; EXPECT_EQ(SPV_ERROR_INTERNAL, AssembleAndLink({body1, body2}, &linked_binary)); - EXPECT_THAT( - GetErrorMessage(), - HasSubstr("Conflicting addressing models: Logical vs Physical32.")); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Conflicting addressing models: Logical (input modules " + "1 through 1) vs Physical32 (input module 2).")); } TEST_F(MemoryModel, MemoryMismatch) { @@ -67,7 +67,38 @@ OpMemoryModel Logical GLSL450 EXPECT_EQ(SPV_ERROR_INTERNAL, AssembleAndLink({body1, body2}, &linked_binary)); EXPECT_THAT(GetErrorMessage(), - HasSubstr("Conflicting memory models: Simple vs GLSL450.")); + HasSubstr("Conflicting memory models: Simple (input modules 1 " + "through 1) vs GLSL450 (input module 2).")); +} + +TEST_F(MemoryModel, FirstLackMemoryModel) { + const std::string body1 = R"( +)"; + const std::string body2 = R"( +OpMemoryModel Logical GLSL450 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Input module 1 is lacking an OpMemoryModel instruction.")); +} + +TEST_F(MemoryModel, SecondLackMemoryModel) { + const std::string body1 = R"( +OpMemoryModel Logical GLSL450 +)"; + const std::string body2 = R"( +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Input module 2 is lacking an OpMemoryModel instruction.")); } } // namespace diff --git a/test/link/partial_linkage_test.cpp b/test/link/partial_linkage_test.cpp index c43b06e5..bf4b508b 100644 --- a/test/link/partial_linkage_test.cpp +++ b/test/link/partial_linkage_test.cpp @@ -26,6 +26,9 @@ using PartialLinkage = spvtest::LinkerTest; TEST_F(PartialLinkage, Allowed) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 LinkageAttributes "bar" Import %3 = OpTypeFloat 32 @@ -34,6 +37,9 @@ OpDecorate %2 LinkageAttributes "bar" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "bar" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 3.1415 @@ -44,9 +50,13 @@ OpDecorate %1 LinkageAttributes "bar" Export LinkerOptions linker_options; linker_options.SetAllowPartialLinkage(true); ASSERT_EQ(SPV_SUCCESS, - AssembleAndLink({body1, body2}, &linked_binary, linker_options)); + AssembleAndLink({body1, body2}, &linked_binary, linker_options)) + << GetErrorMessage(); const std::string expected_res = R"(OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 @@ -64,6 +74,9 @@ OpDecorate %1 LinkageAttributes "foo" Import TEST_F(PartialLinkage, Disallowed) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 LinkageAttributes "bar" Import %3 = OpTypeFloat 32 @@ -72,6 +85,9 @@ OpDecorate %2 LinkageAttributes "bar" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "bar" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 3.1415 diff --git a/test/link/type_match_test.cpp b/test/link/type_match_test.cpp index dae70c16..efc5cf7e 100644 --- a/test/link/type_match_test.cpp +++ b/test/link/type_match_test.cpp @@ -66,6 +66,7 @@ using TypeMatch = spvtest::LinkerTest; "OpCapability Kernel\n" \ "OpCapability Shader\n" \ "OpCapability Addresses\n" \ + "OpMemoryModel Physical64 OpenCL\n" \ "OpDecorate %var LinkageAttributes \"foo\" " \ "{Import,Export}\n" \ "; CHECK: [[baseint:%\\w+]] = OpTypeInt 32 1\n" \ diff --git a/test/link/unique_ids_test.cpp b/test/link/unique_ids_test.cpp index 55c70ea6..16a9b52a 100644 --- a/test/link/unique_ids_test.cpp +++ b/test/link/unique_ids_test.cpp @@ -135,7 +135,8 @@ TEST_F(UniqueIds, UniquelyMerged) { LinkerOptions options; options.SetVerifyIds(true); spv_result_t res = AssembleAndLink(bodies, &linked_binary, options); - EXPECT_EQ(SPV_SUCCESS, res); + ASSERT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), std::string()); } } // namespace diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 5c4223ed..bdddeb89 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp @@ -49,15 +49,18 @@ Options (in lexicographical order): --create-library Link the binaries into a library, keeping all exported symbols. -h, --help - Print this help. + Print this help. --target-env - Set the target environment. Without this flag the target - environment defaults to spv1.5. must be one of - {%s} + Set the environment used for interpreting the inputs. Without + this option the environment defaults to spv1.6. must be + one of {%s}. + NOTE: The SPIR-V version used by the linked binary module + depends only on the version of the inputs, and is not affected + by this option. --verify-ids Verify that IDs in the resulting modules are truly unique. --version - Display linker version information + Display linker version information. )", program, program, target_env_list.c_str()); } @@ -160,10 +163,11 @@ int main(int argc, char** argv) { std::vector linkingResult; spv_result_t status = Link(context, contents, &linkingResult, options); + if (status != SPV_SUCCESS && status != SPV_WARNING) return 1; if (!WriteFile(outFile, "wb", linkingResult.data(), linkingResult.size())) return 1; - return status == SPV_SUCCESS ? 0 : 1; + return 0; } -- cgit v1.2.3 From 58d8b4e29c9e2b314fd715f3c79fdf71296dab33 Mon Sep 17 00:00:00 2001 From: Pierre Moreau Date: Mon, 24 Jan 2022 19:40:57 +0100 Subject: linker: Address conversion error introduced in earlier rework (#4685) * linker: Address conversion error introduced in earlier rework Also rework the code slightly to first compute the new ID bound and validate it, and only then, offset all existing IDs. This makes those two steps clearer, and avoids potentially overflowing the IDs within a module (even if that would have been caught later on). Fixes: c3849565 ("Linker improvements (#4679)") Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/4684 * test/linker: Disable IdsLimit tests for now They are taking too long to run and results in the CI jobs timing out. --- source/link/linker.cpp | 30 ++++++++++++++++++------------ test/link/ids_limit_test.cpp | 6 +++--- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/source/link/linker.cpp b/source/link/linker.cpp index cedc1386..c23a23de 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -169,26 +170,31 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of ShiftIdsInModules should not be null."; - size_t id_bound = modules->front()->IdBound() - 1u; + const size_t id_bound = + std::accumulate(modules->begin(), modules->end(), static_cast(1), + [](const size_t& accumulation, opt::Module* module) { + return accumulation + module->IdBound() - 1u; + }); + if (id_bound > std::numeric_limits::max()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "Too many IDs (" << id_bound + << "): combining all modules would overflow the 32-bit word of the " + "SPIR-V header."; + + *max_id_bound = static_cast(id_bound); + + uint32_t id_offset = modules->front()->IdBound() - 1u; for (auto module_iter = modules->begin() + 1; module_iter != modules->end(); ++module_iter) { Module* module = *module_iter; - module->ForEachInst([&id_bound](Instruction* insn) { - insn->ForEachId([&id_bound](uint32_t* id) { *id += id_bound; }); + module->ForEachInst([&id_offset](Instruction* insn) { + insn->ForEachId([&id_offset](uint32_t* id) { *id += id_offset; }); }); - id_bound += module->IdBound() - 1u; + id_offset += module->IdBound() - 1u; // Invalidate the DefUseManager module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); } - ++id_bound; - if (id_bound > std::numeric_limits::max()) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) - << "Too many IDs (" << id_bound - << "): combining all modules would overflow the 32-bit word of the " - "SPIR-V header."; - - *max_id_bound = static_cast(id_bound); return SPV_SUCCESS; } diff --git a/test/link/ids_limit_test.cpp b/test/link/ids_limit_test.cpp index 21c64b97..846fbef8 100644 --- a/test/link/ids_limit_test.cpp +++ b/test/link/ids_limit_test.cpp @@ -92,14 +92,14 @@ spvtest::Binary CreateBinary(uint32_t id_bound) { }; } -TEST_F(IdsLimit, UnderLimit) { +TEST_F(IdsLimit, DISABLED_UnderLimit) { spvtest::Binary linked_binary; ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); EXPECT_EQ(0x3FFFFFu, linked_binary[3]); } -TEST_F(IdsLimit, OverLimit) { +TEST_F(IdsLimit, DISABLED_OverLimit) { spvtest::Binary& binary = binaries.back(); const uint32_t id_bound = binary[3]; @@ -118,7 +118,7 @@ TEST_F(IdsLimit, OverLimit) { EXPECT_EQ(0x400000u, linked_binary[3]); } -TEST_F(IdsLimit, Overflow) { +TEST_F(IdsLimit, DISABLED_Overflow) { spvtest::Binaries binaries = {CreateBinary(0xFFFFFFFFu), CreateBinary(0x00000002u)}; -- cgit v1.2.3 From e8439c1c9ddc02e8f3aa9726cd765637b34d68fb Mon Sep 17 00:00:00 2001 From: smikims Date: Tue, 25 Jan 2022 06:07:40 -0800 Subject: Avoid infinite recursion in comparison operators on SmallVector (#4681) C++20 automatically adds reversed versions of operator overloads for consideration; in this particular instance this results in infinite recursion, which has now been pointed out elsewhere as a known issue when migrating to C++20. Here we just disable one of the overloads in C++20 mode and let the auto-reversing take care of it for us. --- source/util/small_vector.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/util/small_vector.h b/source/util/small_vector.h index f2c1147b..8f56268a 100644 --- a/source/util/small_vector.h +++ b/source/util/small_vector.h @@ -175,9 +175,12 @@ class SmallVector { return true; } +// Avoid infinite recursion from rewritten operators in C++20 +#if __cplusplus <= 201703L friend bool operator==(const std::vector& lhs, const SmallVector& rhs) { return rhs == lhs; } +#endif friend bool operator!=(const SmallVector& lhs, const std::vector& rhs) { return !(lhs == rhs); -- cgit v1.2.3 From 6938af7f82eb3ccb17131c11f5af6745d346566d Mon Sep 17 00:00:00 2001 From: alan-baker Date: Tue, 25 Jan 2022 10:36:08 -0500 Subject: Vulkan 1.3 (#4686) * Add new environment enum for Vulkan 1,3 * Do not require --allow-localsizeid in Vulkan 1.3 to use LocalSizeId execution mode --- include/spirv-tools/libspirv.h | 5 ++++- source/ext_inst.cpp | 1 + source/spirv_target_env.cpp | 14 ++++++++++++-- source/table.cpp | 1 + source/val/validation_state.cpp | 2 +- test/target_env_test.cpp | 12 ++++++++++++ test/text_to_binary.extension_test.cpp | 2 +- test/val/val_modes_test.cpp | 20 ++++++++++++++++++++ test/val/val_version_test.cpp | 32 +++++++++++++++++++++++++++++++- tools/val/val.cpp | 5 +++-- 10 files changed, 86 insertions(+), 8 deletions(-) diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 9d7de17e..53dde950 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -482,6 +482,7 @@ SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void); // SPV_ENV_VULKAN_1_1 -> SPIR-V 1.3 // SPV_ENV_VULKAN_1_1_SPIRV_1_4 -> SPIR-V 1.4 // SPV_ENV_VULKAN_1_2 -> SPIR-V 1.5 +// SPV_ENV_VULKAN_1_3 -> SPIR-V 1.6 // Consult the description of API entry points for specific rules. typedef enum { SPV_ENV_UNIVERSAL_1_0, // SPIR-V 1.0 latest revision, no other restrictions. @@ -518,7 +519,9 @@ typedef enum { SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. SPV_ENV_UNIVERSAL_1_6, // SPIR-V 1.6 latest revision, no other restrictions. - SPV_ENV_MAX // Keep this as the last enum value. + SPV_ENV_VULKAN_1_3, // Vulkan 1.3 latest revision. + + SPV_ENV_MAX // Keep this as the last enum value. } spv_target_env; // SPIR-V Validator can be parameterized with the following Universal Limits. diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 37b423ee..4e279545 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -97,6 +97,7 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: *pExtInstTable = &kTable_1_0; return SPV_SUCCESS; default: diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index 43d1c3dd..9a038174 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -74,6 +74,8 @@ const char* spvTargetEnvDescription(spv_target_env env) { return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; case SPV_ENV_UNIVERSAL_1_6: return "SPIR-V 1.6"; + case SPV_ENV_VULKAN_1_3: + return "SPIR-V 1.6 (under Vulkan 1.3 semantics)"; case SPV_ENV_MAX: assert(false && "Invalid target environment value."); break; @@ -116,6 +118,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_2: return SPV_SPIRV_VERSION_WORD(1, 5); case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return SPV_SPIRV_VERSION_WORD(1, 6); case SPV_ENV_MAX: assert(false && "Invalid target environment value."); @@ -129,6 +132,7 @@ static const std::pair spvTargetEnvNameMap[] = { {"vulkan1.0", SPV_ENV_VULKAN_1_0}, {"vulkan1.1", SPV_ENV_VULKAN_1_1}, {"vulkan1.2", SPV_ENV_VULKAN_1_2}, + {"vulkan1.3", SPV_ENV_VULKAN_1_3}, {"spv1.0", SPV_ENV_UNIVERSAL_1_0}, {"spv1.1", SPV_ENV_UNIVERSAL_1_1}, {"spv1.2", SPV_ENV_UNIVERSAL_1_2}, @@ -182,7 +186,8 @@ static const VulkanEnv ordered_vulkan_envs[] = { {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)}, {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)}, {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)}, - {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}}; + {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}, + {SPV_ENV_VULKAN_1_3, VULKAN_VER(1, 3), SPIRV_VER(1, 6)}}; bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver, spv_target_env* env) { @@ -222,6 +227,7 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: return true; case SPV_ENV_WEBGPU_0: assert(false && "Deprecated target environment value."); @@ -251,6 +257,7 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return false; case SPV_ENV_OPENCL_1_2: case SPV_ENV_OPENCL_EMBEDDED_1_2: @@ -292,6 +299,7 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return false; case SPV_ENV_OPENGL_4_0: case SPV_ENV_OPENGL_4_1: @@ -330,6 +338,7 @@ bool spvIsValidEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: case SPV_ENV_OPENGL_4_0: case SPV_ENV_OPENGL_4_1: case SPV_ENV_OPENGL_4_2: @@ -365,7 +374,8 @@ std::string spvLogStringForEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: - case SPV_ENV_VULKAN_1_2: { + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: { return "Vulkan"; } case SPV_ENV_UNIVERSAL_1_0: diff --git a/source/table.cpp b/source/table.cpp index a87bbf28..822cefeb 100644 --- a/source/table.cpp +++ b/source/table.cpp @@ -42,6 +42,7 @@ spv_context spvContextCreate(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: break; default: return nullptr; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index c2282611..8c02af1a 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -175,7 +175,7 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, } } - // LocalSizeId is only disallowed without maintainence4. + // LocalSizeId is only disallowed prior to Vulkan 1.3 without maintenance4. switch (env) { case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp index c5646b8f..7917cbfb 100644 --- a/test/target_env_test.cpp +++ b/test/target_env_test.cpp @@ -135,6 +135,7 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 0), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, {VK(1, 0), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.1 cases {VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1}, @@ -143,6 +144,7 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 1), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, {VK(1, 1), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.2 cases {VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2}, @@ -151,7 +153,17 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, {VK(1, 2), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 1.3 cases + {VK(1, 3), SPV(1, 0), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 1), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 2), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 3), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 4), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 5), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 2.0 cases {VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 99.0 cases diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 1f3ff243..6d4e674e 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -959,7 +959,7 @@ INSTANTIATE_TEST_SUITE_P( Combine( Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, - SPV_ENV_VULKAN_1_2), + SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), ValuesIn(std::vector{ {"OpExtension \"SPV_KHR_integer_dot_product\"\n", MakeInstruction(SpvOpExtension, diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 02a61327..a37989b4 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -1178,6 +1178,26 @@ OpFunctionEnd HasSubstr("Expected bool scalar type as Result Type")); } +TEST_F(ValidateMode, LocalSizeIdVulkan1p3DoesNotRequireOption) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_version_test.cpp b/test/val/val_version_test.cpp index a7cb0bad..6b7c4fe8 100644 --- a/test/val/val_version_test.cpp +++ b/test/val/val_version_test.cpp @@ -78,6 +78,7 @@ std::string version(spv_target_env env) { case SPV_ENV_VULKAN_1_2: return "1.5"; case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return "1.6"; default: return "0"; @@ -115,6 +116,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4,vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), @@ -132,6 +134,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -149,6 +152,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -166,6 +170,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -183,6 +188,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -200,6 +206,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -217,6 +224,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -239,6 +247,7 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), @@ -256,6 +265,7 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -273,6 +283,7 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -290,11 +301,30 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), - std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); diff --git a/tools/val/val.cpp b/tools/val/val.cpp index d03262bb..880ce46b 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp @@ -111,7 +111,7 @@ int main(int argc, char** argv) { printf("%s\n", spvSoftwareVersionDetailsString()); printf( "Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n " - "%s\n %s\n %s\n", + "%s\n %s\n %s\n %s\n", spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2), @@ -123,7 +123,8 @@ int main(int argc, char** argv) { spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), spvTargetEnvDescription(SPV_ENV_VULKAN_1_1), spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4), - spvTargetEnvDescription(SPV_ENV_VULKAN_1_2)); + spvTargetEnvDescription(SPV_ENV_VULKAN_1_2), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_3)); continue_processing = false; return_code = 0; } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { -- cgit v1.2.3 From fb9a10cd48bc50a37bf2cf9ad260d0b503177069 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Tue, 25 Jan 2022 13:14:36 -0500 Subject: spirv-opt: add pass to Spread Volatile semantics (#4667) Add a pass to spread Volatile semantics to variables with SMIDNV, WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn decorations or OpLoad for them when the shader model is the ray generation, closest hit, miss, intersection, or callable shaders. This pass can be used for VUID-StandaloneSpirv-VulkanMemoryModel-04678 and VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V Validation" section of Vulkan spec "Appendix A: Vulkan Environment for SPIR-V"). Handle variables used by multiple entry points: 1. Update error check to make it working regardless of the order of entry points. 2. For a variable, if it is used by two entry points E1 and E2 and it needs the Volatile semantics for E1 while it does not for E2 - If VulkanMemoryModel capability is enabled, which means we have to set memory operation of load instructions for the variable, we update load instructions in E1, but do not update the ones in E2. - If VulkanMemoryModel capability is disabled, which means we have to add Volatile decoration for the variable, we report an error because E1 needs to add Volatile decoration for the variable while E2 does not. For the simplicity of the implementation, we assume that all functions other than entry point functions are inlined. --- Android.mk | 1 + BUILD.gn | 2 + include/spirv-tools/optimizer.hpp | 13 + source/opt/CMakeLists.txt | 2 + source/opt/optimizer.cpp | 7 + source/opt/passes.h | 1 + source/opt/spread_volatile_semantics.cpp | 314 ++++++++ source/opt/spread_volatile_semantics.h | 110 +++ test/opt/CMakeLists.txt | 1 + test/opt/spread_volatile_semantics_test.cpp | 1118 +++++++++++++++++++++++++++ tools/opt/opt.cpp | 10 + utils/check_copyright.py | 2 +- 12 files changed, 1580 insertions(+), 1 deletion(-) create mode 100644 source/opt/spread_volatile_semantics.cpp create mode 100644 source/opt/spread_volatile_semantics.h create mode 100644 test/opt/spread_volatile_semantics_test.cpp diff --git a/Android.mk b/Android.mk index 49af8fa8..2aee9288 100644 --- a/Android.mk +++ b/Android.mk @@ -165,6 +165,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/scalar_replacement_pass.cpp \ source/opt/set_spec_constant_default_value_pass.cpp \ source/opt/simplification_pass.cpp \ + source/opt/spread_volatile_semantics.cpp \ source/opt/ssa_rewrite_pass.cpp \ source/opt/strength_reduction_pass.cpp \ source/opt/strip_debug_info_pass.cpp \ diff --git a/BUILD.gn b/BUILD.gn index 09176e6f..4d154caf 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -732,6 +732,8 @@ static_library("spvtools_opt") { "source/opt/set_spec_constant_default_value_pass.h", "source/opt/simplification_pass.cpp", "source/opt/simplification_pass.h", + "source/opt/spread_volatile_semantics.cpp", + "source/opt/spread_volatile_semantics.h", "source/opt/ssa_rewrite_pass.cpp", "source/opt/ssa_rewrite_pass.h", "source/opt/strength_reduction_pass.cpp", diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 5d173c11..3e63037c 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -835,6 +835,19 @@ Optimizer::PassToken CreateFixStorageClassPass(); // inclusive. Optimizer::PassToken CreateGraphicsRobustAccessPass(); +// Create a pass to spread Volatile semantics to variables with SMIDNV, +// WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, +// SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn +// decorations or OpLoad for them when the shader model is the ray generation, +// closest hit, miss, intersection, or callable. This pass can be used for +// VUID-StandaloneSpirv-VulkanMemoryModel-04678 and +// VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V +// Validation" section of Vulkan spec "Appendix A: Vulkan Environment for +// SPIR-V"). When the SPIR-V version is 1.6 or above, the pass also spreads +// the Volatile semantics to a variable with HelperInvocation BuiltIn decoration +// in the fragement shader. +Optimizer::PassToken CreateSpreadVolatileSemanticsPass(); + // Create a pass to replace a descriptor access using variable index. // This pass replaces every access using a variable index to array variable // |desc| that has a DescriptorSet and Binding decorations with a constant diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index f01766c7..7508dc02 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -108,6 +108,7 @@ set(SPIRV_TOOLS_OPT_SOURCES scalar_replacement_pass.h set_spec_constant_default_value_pass.h simplification_pass.h + spread_volatile_semantics.h ssa_rewrite_pass.h strength_reduction_pass.h strip_debug_info_pass.h @@ -215,6 +216,7 @@ set(SPIRV_TOOLS_OPT_SOURCES scalar_replacement_pass.cpp set_spec_constant_default_value_pass.cpp simplification_pass.cpp + spread_volatile_semantics.cpp ssa_rewrite_pass.cpp strength_reduction_pass.cpp strip_debug_info_pass.cpp diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index d8daf82c..330093e4 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -324,6 +324,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateLocalAccessChainConvertPass()); } else if (pass_name == "replace-desc-array-access-using-var-index") { RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass()); + } else if (pass_name == "spread-volatile-semantics") { + RegisterPass(CreateSpreadVolatileSemanticsPass()); } else if (pass_name == "descriptor-scalar-replacement") { RegisterPass(CreateDescriptorScalarReplacementPass()); } else if (pass_name == "eliminate-dead-code-aggressive") { @@ -976,6 +978,11 @@ Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() { MakeUnique()); } +Optimizer::PassToken CreateSpreadVolatileSemanticsPass() { + return MakeUnique( + MakeUnique()); +} + Optimizer::PassToken CreateDescriptorScalarReplacementPass() { return MakeUnique( MakeUnique()); diff --git a/source/opt/passes.h b/source/opt/passes.h index 3266dbd0..d51c306e 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -71,6 +71,7 @@ #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" #include "source/opt/simplification_pass.h" +#include "source/opt/spread_volatile_semantics.h" #include "source/opt/ssa_rewrite_pass.h" #include "source/opt/strength_reduction_pass.h" #include "source/opt/strip_debug_info_pass.h" diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp new file mode 100644 index 00000000..17a4c725 --- /dev/null +++ b/source/opt/spread_volatile_semantics.cpp @@ -0,0 +1,314 @@ +// Copyright (c) 2022 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/opt/spread_volatile_semantics.h" + +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_builder.h" +#include "source/spirv_constant.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpDecorateInOperandBuiltinDecoration = 2u; +const uint32_t kOpLoadInOperandMemoryOperands = 1u; +const uint32_t kOpEntryPointInOperandEntryPoint = 1u; +const uint32_t kOpEntryPointInOperandInterface = 3u; + +bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id, uint32_t built_in) { + return decoration_manager->FindDecoration( + var_id, SpvDecorationBuiltIn, [built_in](const Instruction& inst) { + return built_in == inst.GetSingleWordInOperand( + kOpDecorateInOperandBuiltinDecoration); + }); +} + +bool IsBuiltInForRayTracingVolatileSemantics(uint32_t built_in) { + switch (built_in) { + case SpvBuiltInSMIDNV: + case SpvBuiltInWarpIDNV: + case SpvBuiltInSubgroupSize: + case SpvBuiltInSubgroupLocalInvocationId: + case SpvBuiltInSubgroupEqMask: + case SpvBuiltInSubgroupGeMask: + case SpvBuiltInSubgroupGtMask: + case SpvBuiltInSubgroupLeMask: + case SpvBuiltInSubgroupLtMask: + return true; + default: + return false; + } +} + +bool HasBuiltinForRayTracingVolatileSemantics( + analysis::DecorationManager* decoration_manager, uint32_t var_id) { + return decoration_manager->FindDecoration( + var_id, SpvDecorationBuiltIn, [](const Instruction& inst) { + uint32_t built_in = + inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration); + return IsBuiltInForRayTracingVolatileSemantics(built_in); + }); +} + +bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id) { + return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile); +} + +bool HasOnlyEntryPointsAsFunctions(IRContext* context, Module* module) { + std::unordered_set entry_function_ids; + for (Instruction& entry_point : module->entry_points()) { + entry_function_ids.insert( + entry_point.GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint)); + } + for (auto& function : *module) { + if (entry_function_ids.find(function.result_id()) == + entry_function_ids.end()) { + std::string message( + "Functions of SPIR-V for spread-volatile-semantics pass input must " + "be inlined except entry points"); + message += "\n " + function.DefInst().PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return false; + } + } + return true; +} + +} // namespace + +Pass::Status SpreadVolatileSemantics::Process() { + if (!HasOnlyEntryPointsAsFunctions(context(), get_module())) { + return Status::Failure; + } + + const bool is_vk_memory_model_enabled = + context()->get_feature_mgr()->HasCapability( + SpvCapabilityVulkanMemoryModel); + CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled); + + // If VulkanMemoryModel capability is not enabled, we have to set Volatile + // decoration for interface variables instead of setting Volatile for load + // instructions. If an interface (or pointers to it) is used by two load + // instructions in two entry points and one must be volatile while another + // is not, we have to report an error for the conflict. + if (!is_vk_memory_model_enabled && + HasInterfaceInConflictOfVolatileSemantics()) { + return Status::Failure; + } + + return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled); +} + +Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled) { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + auto entry_function_ids = + EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id()); + if (entry_function_ids.empty()) { + continue; + } + + if (is_vk_memory_model_enabled) { + SetVolatileForLoadsInEntries(&var, entry_function_ids); + } else { + DecorateVarWithVolatile(&var); + } + status = Status::SuccessWithChange; + } + return status; +} + +bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + return !VisitLoadsOfPointersToVariableInEntries( + var_id, + [](Instruction* load) { + // If it has a load without volatile memory operand, finish traversal + // and return false. + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + return false; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + return (memory_operands & SpvMemoryAccessVolatileMask) != 0; + }, + {entry_function_id}); +} + +bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() { + for (Instruction& entry_point : get_module()->entry_points()) { + SpvExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() && + !IsTargetForVolatileSemantics(var_id, execution_model) && + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id); + context()->EmitErrorMessage( + "Variable is a target for Volatile semantics for an entry point, " + "but it is not for another entry point", + inst); + return true; + } + } + } + return false; +} + +void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) { + var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id}; + return; + } + itr->second.insert(entry_function_id); +} + +void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled) { + for (Instruction& entry_point : get_module()->entry_points()) { + SpvExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!IsTargetForVolatileSemantics(var_id, execution_model)) { + continue; + } + if (is_vk_memory_model_enabled || + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + MarkVolatileSemanticsForVariable(var_id, &entry_point); + } + } + } +} + +void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + uint32_t var_id = var->result_id(); + if (HasVolatileDecoration(decoration_manager, var_id)) { + return; + } + get_decoration_mgr()->AddDecoration( + SpvOpDecorate, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvDecorationVolatile}}}); +} + +bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function& handle_load, + const std::unordered_set& entry_function_ids) { + std::vector worklist({var_id}); + auto* def_use_mgr = context()->get_def_use_mgr(); + while (!worklist.empty()) { + uint32_t ptr_id = worklist.back(); + worklist.pop_back(); + bool finish_traversal = !def_use_mgr->WhileEachUser( + ptr_id, [this, &worklist, &ptr_id, handle_load, + &entry_function_ids](Instruction* user) { + BasicBlock* block = context()->get_instr_block(user); + if (block == nullptr || + entry_function_ids.find(block->GetParent()->result_id()) == + entry_function_ids.end()) { + return true; + } + + if (user->opcode() == SpvOpAccessChain || + user->opcode() == SpvOpInBoundsAccessChain || + user->opcode() == SpvOpPtrAccessChain || + user->opcode() == SpvOpInBoundsPtrAccessChain || + user->opcode() == SpvOpCopyObject) { + if (ptr_id == user->GetSingleWordInOperand(0)) + worklist.push_back(user->result_id()); + return true; + } + + if (user->opcode() != SpvOpLoad) { + return true; + } + + return handle_load(user); + }); + if (finish_traversal) return false; + } + return true; +} + +void SpreadVolatileSemantics::SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set& entry_function_ids) { + // Set Volatile memory operand for all load instructions if they do not have + // it. + VisitLoadsOfPointersToVariableInEntries( + var->result_id(), + [](Instruction* load) { + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + load->AddOperand( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessVolatileMask}}); + return true; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + memory_operands |= SpvMemoryAccessVolatileMask; + load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands}); + return true; + }, + entry_function_ids); +} + +bool SpreadVolatileSemantics::IsTargetForVolatileSemantics( + uint32_t var_id, SpvExecutionModel execution_model) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + if (execution_model == SpvExecutionModelFragment) { + return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) && + HasBuiltinDecoration(decoration_manager, var_id, + SpvBuiltInHelperInvocation); + } + + if (execution_model == SpvExecutionModelIntersectionKHR || + execution_model == SpvExecutionModelIntersectionNV) { + if (HasBuiltinDecoration(decoration_manager, var_id, + SpvBuiltInRayTmaxKHR)) { + return true; + } + } + + switch (execution_model) { + case SpvExecutionModelRayGenerationKHR: + case SpvExecutionModelClosestHitKHR: + case SpvExecutionModelMissKHR: + case SpvExecutionModelCallableKHR: + case SpvExecutionModelIntersectionKHR: + return HasBuiltinForRayTracingVolatileSemantics(decoration_manager, + var_id); + default: + return false; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h new file mode 100644 index 00000000..3d0a1839 --- /dev/null +++ b/source/opt/spread_volatile_semantics.h @@ -0,0 +1,110 @@ +// Copyright (c) 2022 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_OPT_SPREAD_VOLATILE_SEMANTICS_H_ +#define SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SpreadVolatileSemantics : public Pass { + public: + SpreadVolatileSemantics() {} + + const char* name() const override { return "spread-volatile-semantics"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations | + IRContext::kAnalysisInstrToBlockMapping; + } + + private: + // Iterates interface variables and spreads the Volatile semantics if it has + // load instructions for the Volatile semantics. + Pass::Status SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled); + + // Returns whether |var_id| is the result id of a target builtin variable for + // the volatile semantics for |execution_model| based on the Vulkan spec + // VUID-StandaloneSpirv-VulkanMemoryModel-04678 or + // VUID-StandaloneSpirv-VulkanMemoryModel-04679. + bool IsTargetForVolatileSemantics(uint32_t var_id, + SpvExecutionModel execution_model); + + // Collects interface variables that need the volatile semantics. + // |is_vk_memory_model_enabled| is true if VulkanMemoryModel capability is + // enabled. + void CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled); + + // Reports an error if an interface variable is used by two entry points and + // it needs the Volatile decoration for one but not for another. Returns true + // if the error must be reported. + bool HasInterfaceInConflictOfVolatileSemantics(); + + // Returns whether the variable whose result is |var_id| is used by a + // non-volatile load or a pointer to it is used by a non-volatile load in + // |entry_point| or not. + bool IsTargetUsedByNonVolatileLoadInEntryPoint(uint32_t var_id, + Instruction* entry_point); + + // Visits load instructions of pointers to variable whose result id is + // |var_id| if the load instructions are in entry points whose + // function id is one of |entry_function_ids|. |handle_load| is a function to + // do some actions for the load instructions. Finishes the traversal and + // returns false if |handle_load| returns false for a load instruction. + // Otherwise, returns true after running |handle_load| for all the load + // instructions. + bool VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function& handle_load, + const std::unordered_set& entry_function_ids); + + // Sets Memory Operands of OpLoad instructions that load |var| or pointers + // of |var| as Volatile if the function id of the OpLoad instruction is + // included in |entry_function_ids|. + void SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set& entry_function_ids); + + // Adds OpDecorate Volatile for |var| if it does not exist. + void DecorateVarWithVolatile(Instruction* var); + + // Returns a set of entry function ids to spread the volatile semantics for + // the variable with the result id |var_id|. + std::unordered_set EntryFunctionsToSpreadVolatileSemanticsForVar( + uint32_t var_id) { + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) return {}; + return itr->second; + } + + // Specifies that we have to spread the volatile semantics for the + // variable with the result id |var_id| for the entry point |entry_point|. + void MarkVolatileSemanticsForVariable(uint32_t var_id, + Instruction* entry_point); + + // Result ids of variables to entry function ids for the volatile semantics + // spread. + std::unordered_map> + var_ids_to_entry_fn_for_volatile_semantics_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index d70b9235..759d4237 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -91,6 +91,7 @@ add_spvtools_unittest(TARGET opt scalar_replacement_test.cpp set_spec_const_default_value_test.cpp simplification_test.cpp + spread_volatile_semantics_test.cpp strength_reduction_test.cpp strip_debug_info_test.cpp strip_nonsemantic_info_test.cpp diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp new file mode 100644 index 00000000..83b2dcfa --- /dev/null +++ b/test/opt/spread_volatile_semantics_test.cpp @@ -0,0 +1,1118 @@ +// Copyright (c) 2022 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 + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +struct ExecutionModelAndBuiltIn { + const char* execution_model; + const char* built_in; + const bool use_v4uint; +}; + +using AddVolatileDecorationTest = + PassTest<::testing::TestWithParam>; + +TEST_P(AddVolatileDecorationTest, InMain) { + const auto& tc = GetParam(); + const std::string execution_model(tc.execution_model); + const std::string built_in(tc.built_in); + const std::string var_type = + tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint"; + const std::string var_load_type = tc.use_v4uint ? "%v4uint" : "%uint"; + + const std::string text = + std::string(R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint )") + + execution_model + std::string(R"( %main "main" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +)") + std::string(R"( +; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") + + built_in + std::string(R"( +; CHECK: OpDecorate [[var]] Volatile +OpDecorate %var BuiltIn )") + built_in + std::string(R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%var = OpVariable )") + + var_type + std::string(R"( Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%load = OpLoad )") + var_load_type + std::string(R"( %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"); + + SinglePassRunAndMatch(text, true); +} + +INSTANTIATE_TEST_SUITE_P( + AddVolatileDecoration, AddVolatileDecorationTest, + ::testing::ValuesIn(std::vector{ + {"RayGenerationKHR", "SubgroupSize", false}, + {"RayGenerationKHR", "SubgroupLocalInvocationId", false}, + {"RayGenerationKHR", "SubgroupEqMask", true}, + {"ClosestHitKHR", "SubgroupLocalInvocationId", true}, + {"IntersectionKHR", "SubgroupEqMask", true}, + {"MissKHR", "SubgroupGeMask", true}, + {"CallableKHR", "SubgroupGtMask", true}, + {"RayGenerationKHR", "SubgroupLeMask", true}, + })); + +using SetLoadVolatileTest = + PassTest<::testing::TestWithParam>; + +TEST_P(SetLoadVolatileTest, InMain) { + const auto& tc = GetParam(); + const std::string execution_model(tc.execution_model); + const std::string built_in(tc.built_in); + + const std::string var_type = + tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint"; + const std::string var_value = tc.use_v4uint ? std::string(R"( +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[var]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr]] Volatile +%ptr = OpAccessChain %_ptr_Input_uint %var %int_0 +%var_value = OpLoad %uint %ptr)") + : std::string(R"( +; CHECK: OpLoad {{%\w+}} [[var]] Volatile +%var_value = OpLoad %uint %var)"); + + const std::string text = std::string(R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +OpMemoryModel Logical Vulkan +OpEntryPoint )") + execution_model + + std::string(R"( %main "main" %var +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +)") + std::string(R"( +; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") + + built_in + std::string(R"( +OpDecorate %var BuiltIn )") + built_in + + std::string(R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%var = OpVariable )") + var_type + + std::string(R"( Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +)") + var_value + std::string(R"( +%test = OpIAdd %uint %var_value %20 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %test +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"); + + SinglePassRunAndMatch(text, true); +} + +INSTANTIATE_TEST_SUITE_P( + SetLoadVolatile, SetLoadVolatileTest, + ::testing::ValuesIn(std::vector{ + {"RayGenerationKHR", "SubgroupSize", false}, + {"RayGenerationKHR", "SubgroupLocalInvocationId", false}, + {"RayGenerationKHR", "SubgroupEqMask", true}, + {"ClosestHitKHR", "SubgroupLocalInvocationId", true}, + {"IntersectionKHR", "SubgroupEqMask", true}, + {"MissKHR", "SubgroupGeMask", true}, + {"CallableKHR", "SubgroupGtMask", true}, + {"RayGenerationKHR", "SubgroupLeMask", true}, + })); + +using VolatileSpreadTest = PassTest<::testing::Test>; + +TEST_F(VolatileSpreadTest, SpreadVolatileForHelperInvocation) { + const std::string text = + R"( +OpCapability Shader +OpCapability DemoteToHelperInvocation +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft + +; CHECK: OpDecorate [[var:%\w+]] BuiltIn HelperInvocation +; CHECK: OpDecorate [[var]] Volatile +OpDecorate %var BuiltIn HelperInvocation + +%bool = OpTypeBool +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%_ptr_Input_bool = OpTypePointer Input %bool +%var = OpVariable %_ptr_Input_bool Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +%load = OpLoad %bool %var +OpDemoteToHelperInvocation +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, MultipleExecutionModel) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]] +; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize +; CHECK: OpDecorate [[var]] Volatile +; CHECK-NOT: OpDecorate {{%\w+}} Volatile +OpDecorate %var BuiltIn SubgroupSize + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %62 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, VarUsedInMultipleEntryPoints) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %ClosestHit "ClosestHit" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]] +; CHECK: OpEntryPoint ClosestHitNV {{%\w+}} "ClosestHit" [[var]] +; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize +; CHECK: OpDecorate [[var]] Volatile +; CHECK-NOT: OpDecorate {{%\w+}} Volatile +OpDecorate %var BuiltIn SubgroupSize + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%ClosestHit = OpFunction %void None %3 +%45 = OpLabel +%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%40 = OpLoad %uint %var +%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40 +%43 = OpLoad %13 %42 +%47 = OpImageRead %v4float %43 %25 +%59 = OpCompositeExtract %float %47 0 +%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %51 %59 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +class VolatileSpreadErrorTest : public PassTest<::testing::Test> { + public: + VolatileSpreadErrorTest() + : consumer_([this](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + if (!error_message_.empty()) error_message_ += "\n"; + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + error_message_ += "ERROR"; + break; + case SPV_MSG_WARNING: + error_message_ += "WARNING"; + break; + case SPV_MSG_INFO: + error_message_ += "INFO"; + break; + case SPV_MSG_DEBUG: + error_message_ += "DEBUG"; + break; + } + error_message_ += + ": " + std::to_string(position.index) + ": " + message; + }) {} + + Pass::Status RunPass(const std::string& text) { + std::unique_ptr context_ = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text); + if (!context_.get()) return Pass::Status::Failure; + + PassManager manager; + manager.SetMessageConsumer(consumer_); + manager.AddPass(); + + return manager.Run(context_.get()); + } + + std::string GetErrorMessage() const { return error_message_; } + + void TearDown() override { error_message_.clear(); } + + private: + spvtools::MessageConsumer consumer_; + std::string error_message_; +}; + +TEST_F(VolatileSpreadErrorTest, VarUsedInMultipleExecutionModelError) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Variable is a target for Volatile semantics for an entry " + "point, but it is not for another entry point"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, + VarUsedInMultipleReverseOrderExecutionModelError) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Variable is a target for Volatile semantics for an entry " + "point, but it is not for another entry point"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, FunctionNotInlined) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %ClosestHit "ClosestHit" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%NotInlined = OpFunction %void None %3 +%32 = OpLabel +OpReturn +OpFunctionEnd + +%ClosestHit = OpFunction %void None %3 +%45 = OpLabel +%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%40 = OpLoad %uint %49 +%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40 +%43 = OpLoad %13 %42 +%47 = OpImageRead %v4float %43 %25 +%59 = OpCompositeExtract %float %47 0 +%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %51 %59 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Functions of SPIR-V for spread-volatile-semantics pass " + "input must be inlined except entry points"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, VarNotUsedInEntryPointForVolatile) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK-NOT: OpDecorate {{%\w+}} Volatile + +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, RecursivelySpreadVolatile) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask +; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask +OpDecorate %var0 BuiltIn SubgroupEqMask +OpDecorate %var1 BuiltIn SubgroupGeMask + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%_ptr_Input_uint = OpTypePointer Input %uint +%var0 = OpVariable %_ptr_Input_v4uint Input +%var1 = OpVariable %_ptr_Input_v4uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel + +; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0 +%20 = OpLoad %uint %19 + +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1 + +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%24 = OpLoad %uint %19 + +; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]] +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile +%18 = OpCopyObject %_ptr_Input_v4uint %var0 +%21 = OpAccessChain %_ptr_Input_uint %18 %int_1 +%26 = OpLoad %uint %21 + +%28 = OpIAdd %uint %24 %26 +%30 = OpConvertUToF %float %28 + +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile +%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1 +%33 = OpLoad %uint %32 + +%34 = OpConvertUToF %float %33 +%35 = OpFAdd %float %34 %30 +%36 = OpFAdd %float %35 %29 +OpStore %31 %36 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(VolatileSpreadTest, SpreadVolatileOnlyForTargetEntryPoints) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpCapability VulkanMemoryModelDeviceScopeKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1 +OpEntryPoint GLCompute %compute "Compute" %var0 %var1 +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask +; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask +OpDecorate %var0 BuiltIn SubgroupEqMask +OpDecorate %var1 BuiltIn SubgroupGeMask + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%_ptr_Input_uint = OpTypePointer Input %uint +%var0 = OpVariable %_ptr_Input_v4uint Input +%var1 = OpVariable %_ptr_Input_v4uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel + +; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0 +%20 = OpLoad %uint %19 + +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1 + +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%24 = OpLoad %uint %19 + +; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]] +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile +%18 = OpCopyObject %_ptr_Input_v4uint %var0 +%21 = OpAccessChain %_ptr_Input_uint %18 %int_1 +%26 = OpLoad %uint %21 + +%28 = OpIAdd %uint %24 %26 +%30 = OpConvertUToF %float %28 + +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile +%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1 +%33 = OpLoad %uint %32 + +%34 = OpConvertUToF %float %33 +%35 = OpFAdd %float %34 %30 +%36 = OpFAdd %float %35 %29 +OpStore %31 %36 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel + +; CHECK-NOT: OpLoad {{%\w+}} {{%\w+}} Volatile +%62 = OpLoad %v4uint %var0 +%63 = OpLoad %v4uint %var1 +%64 = OpIAdd %v4uint %62 %63 +%65 = OpCompositeExtract %uint %64 0 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %65 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 3dff43f6..5610b248 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -168,6 +168,16 @@ Options (in lexicographical order):)", with a switch that has a case for every possible value of the index.)"); printf(R"( + --spread-volatile-semantics + Spread Volatile semantics to variables with SMIDNV, WarpIDNV, + SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, + SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask + BuiltIn decorations or OpLoad for them when the shader model is + ray generation, closest hit, miss, intersection, or callable. + For the SPIR-V version is 1.6 or above, it also spreads Volatile + semantics to a variable with HelperInvocation BuiltIn decoration + in the fragement shader.)"); + printf(R"( --descriptor-scalar-replacement Replaces every array variable |desc| that has a DescriptorSet and Binding decorations with a new variable for each element of diff --git a/utils/check_copyright.py b/utils/check_copyright.py index 49892ee6..b6dc933e 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -43,7 +43,7 @@ AUTHORS = ['The Khronos Group Inc.', 'ZHOU He'] CURRENT_YEAR='2021' -YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)' +YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021|2022)' COPYRIGHT_RE = re.compile( 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS))) -- cgit v1.2.3 From 5003644f554bafe7f002699d12581d6f903386f3 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 25 Jan 2022 20:44:03 +0000 Subject: Fix roll_deps.sh (#4687) Two deps changes branch names, and roll_deps.sh must be updated. We also don't want the script to fail just because one of the deps is already up-to-date. Set `+e` before rolling the deps. --- utils/roll_deps.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh index f61f2a31..cef8b526 100755 --- a/utils/roll_deps.sh +++ b/utils/roll_deps.sh @@ -23,9 +23,9 @@ set -eo pipefail effcee_dir="external/effcee/" effcee_trunk="origin/main" googletest_dir="external/googletest/" -googletest_trunk="origin/master" +googletest_trunk="origin/main" re2_dir="external/re2/" -re2_trunk="origin/master" +re2_trunk="origin/main" spirv_headers_dir="external/spirv-headers/" spirv_headers_trunk="origin/master" @@ -41,6 +41,7 @@ fi old_head=$(git rev-parse HEAD) +set +e roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}" roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}" roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}" -- cgit v1.2.3 From d492ebded7fa207c98f8ad3be968c52037418ce6 Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Wed, 26 Jan 2022 08:50:58 -0500 Subject: Update DEPS file (#4688) * Roll external/googletest/ bf0701daa..f45d5865e (47 commits) https://github.com/google/googletest/compare/bf0701daa9f5...f45d5865ed0b $ git log bf0701daa..f45d5865e --date=short --no-merges --format='%ad %ae %s' 2022-01-24 absl-team Make recreate_environments_when_repeating=false the default. 2022-01-19 absl-team Factor out AssertionResult into dedicated gtest-assertion-result header + implementation files to prevent cyclic includes between gtest.h and gtest_pred_impl.h 2022-01-18 absl-team Consistently apply IWYU pragmas across googletest and googlemock headers 2022-01-14 dinor Fix reference to source file in gmock FAQ 2022-01-14 dmauro Update test Docker image to GCC 11.2, Clang 14 (prerelease), CMake 3.22.1, and Bazel 4.2.2 2022-01-13 ayush854032 FIX #2174 -- remove `DEBUG_POSTFIX` 2022-01-12 absl-team Fix run-on sentence 2022-01-06 hgsilverman Fix a typo in comments. 2021-12-22 hgsilverman Use normal for loop instead of accumulate. 2021-12-17 hgsilverman Improve code readablity. 2021-12-17 hgsilverman Apply requested changes to preserve old behavior. 2021-12-03 hgsilverman Reimplement MatchesFilter with new interfaces. 2021-11-23 hgsilverman Apply requested changes. 2021-10-17 hgsilverman Get rid of redundant filter matching code 2021-10-17 hgsilverman Add comments describing the behavior of filters 2021-10-17 hgsilverman Process filter string once instead of per test 2022-01-05 bsilver16384 Remove another GTEST_DISALLOW_ASSIGN_ that crept in 2022-01-04 4789010+ramkumar-kr Update example to reflect the tests below 2022-01-01 ayush854032 FIX #3719 -- Fix `clang` conversion warnings 2021-12-27 absl-team Include the param names in the generated description of the MATCHER_P matchers. 2021-12-22 absl-team Clarify "package" means "Bazel package", and promote `testonly=True` rather than `testing` sub-directory. 2021-12-22 dmauro Makes the Python imports consistently use full paths from the repository root, unifying the behavior between Bazel and CMake 2021-12-21 dmauro Makes TestForDeathTest.CRTDebugDeath only run when _DEBUG is defined 2021-12-21 philip.j.m link to regex on qnx in cmake 2021-12-15 dinor Release tests for UnitTestOptions::MatchesFilter 2021-12-07 absl-team Add NOLINT to address modernize-use-trailing-return-type in TEST_F uses 2021-12-04 ayush854032 Allow to specify a random seed independently of whether shuffling is requested 2021-12-03 absl-team Support std::reference_wapper as a smart pointer type in gmock 2021-12-02 absl-team Fix typo in GoogleMock Cookbook. 2021-11-29 absl-team Work around Android KitKat tzset bug 2021-11-25 ludger.paehler Fix typo in the queue test. 2021-11-16 denis.samoylov Suppress unused-value warning 2021-11-16 93249401+assafpr fixed branch from master to main in readme.md 2021-11-16 93249401+assafpr Update README.md 2021-11-16 krystian.kuzniarek fix a typo 2021-11-15 cramertj Clarify "Times() after X" error message 2021-11-15 dmauro Update URLs in Bazel Quickstart 2021-11-12 absl-team Fix typo in gmock-matchers_test.cc: CanDescribeNegationOfExpectingOneElment => CanDescribeNegationOfExpectingOneElement 2021-11-10 absl-team Googletest export 2021-11-09 dmauro Googletest export 2021-11-10 memmenlauer googletest/src/gtest-port.cc: Added GetLastError() on Windows for CreateThread() 2021-11-09 absl-team Googletest export 2021-11-08 absl-team Googletest export 2021-11-08 absl-team Googletest export 2021-11-04 absl-team Googletest export 2021-11-05 raul.tambre Replace use of deprecated std::iterator 2021-10-20 h.zeller Start a simple github actions CI Created with: roll-dep external/googletest * Roll external/re2/ 4244cd1cb..611baecbc (42 commits) https://github.com/google/re2/compare/4244cd1cb492...611baecbcedc $ git log 4244cd1cb..611baecbc --date=short --no-merges --format='%ad %ae %s' 2022-01-24 junyer Update to the latest version of FuzzedDataProvider. 2022-01-19 junyer Replace the build job with the build-appleclang job. 2022-01-18 junyer Address a `-Wunused-but-set-variable' warning from Clang 13.x. 2022-01-18 junyer Fix the build job on macos-latest. 2022-01-17 junyer Remove GCC 4.x and 5.x from the CI configuration. 2022-01-16 stefano Don't specify the `-std` flag in Makefile or re2.pc. 2022-01-12 junyer Use Python 3 shebangs for make_unicode_*.py. 2021-12-24 andrei.bencsik Fix CMake packaging for installation. 2021-11-18 junyer Remove a redundant map access. 2021-11-02 junyer Produce static-testinstall and shared-testinstall binaries. 2021-10-27 junyer Precompute bit_state_text_max_size during compilation. 2021-10-27 junyer Fix another latent bug in Regexp::Walker::Reset(). 2021-10-25 junyer Update Unicode data to 14.0.0. 2021-10-25 junyer Make the fuzzer check for large subexpressions. 2021-10-23 junyer Address a `-Wshadow' warning. 2021-10-19 junyer Don't assume that iterators can be compared. 2021-10-18 junyer Switch back to native C++ rules. 2021-09-02 junyer Crudely limit the use of 'k', 'K', 's' and 'S' when fuzzing. 2021-09-02 junyer Inspect substrings with a Walker<> instead of Prefilter. 2021-08-31 junyer thread_local support in MinGW seems to be buggy. 2021-08-26 junyer Use SRWLOCK_INIT instead of InitializeSRWLock(). 2021-08-23 junyer Add Clang 13 to the build matrix. 2021-08-13 junyer Tweak the OnePass versus DFA cutoff. 2021-08-09 junyer Improve the D wrapper details. 2021-08-09 junyer Mention the D wrapper. 2021-08-03 junyer Fix breakage when RE2_USE_ICU is defined. 2021-08-03 junyer Permit Unicode characters beyond ASCII in capture names. 2021-07-24 junyer Further reduce the maximum repeat count when fuzzing. 2021-07-08 junyer Add a SECURITY.md file. 2021-07-07 junyer Note that case-insensitive prefixes are already lowercase. 2021-07-06 junyer Tidy up BuildShiftDFA() a bit more. 2021-07-06 junyer Flip the hot loops to do-while. 2021-07-06 junyer Optimise the hot loop some more for Clang. 2021-07-06 junyer Work around a code generation issue seen with Clang. 2021-07-05 junyer Fix a couple of style nits. 2021-07-04 junyer Fix a bug that affects 32-bit platforms. 2021-07-03 junyer Implement case-insensitive prefix acceleration. 2021-07-02 junyer Add Search_Easy2_Cached* benchmarks. 2021-06-30 junyer Migration from master to main. 2021-06-23 junyer Revert "Yikes. int64_t{1}<<31 doesn't actually make sense." 2021-06-23 junyer Fix GetCachedProg() for OnePass. 2021-06-23 junyer Yikes. int64_t{1}<<31 doesn't actually make sense. Created with: roll-dep external/re2 * Roll external/spirv-headers/ eddd4dfc9..b42ba6d92 (2 commits) https://github.com/KhronosGroup/SPIRV-Headers/compare/eddd4dfc930f...b42ba6d92faf $ git log eddd4dfc9..b42ba6d92 --date=short --no-merges --format='%ad %ae %s' 2022-01-17 michael.kinsner Allocate additional loop control bit for upcoming Intel extension 2021-11-16 dragonjoker59 Add ShaderWriter as SPIR-V generation tool. Created with: roll-dep external/spirv-headers --- DEPS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 88cb4cad..81e1b5d6 100644 --- a/DEPS +++ b/DEPS @@ -4,9 +4,9 @@ vars = { 'github': 'https://github.com', 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133', - 'googletest_revision': 'bf0701daa9f5b30e5882e2f8f9a5280bcba87e77', - 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', - 'spirv_headers_revision': 'eddd4dfc930f1374a70797460240a501c7d333f7', + 'googletest_revision': 'f45d5865ed0b2b8912244627cdf508a24cc6ccb4', + 're2_revision': '611baecbcedc9cec1f46e38616b6d8880b676c03', + 'spirv_headers_revision': 'b42ba6d92faf6b4938e6f22ddd186dbdacc98d78', } deps = { -- cgit v1.2.3 From 73735db943d7165d725883a1da0ad9eac79c1e34 Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Wed, 26 Jan 2022 09:16:33 -0500 Subject: Update CHANGES --- CHANGES | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 4afd14a6..4756035e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,26 @@ Revision history for SPIRV-Tools -v2021.5-dev 2021-11-11 - - Start v2021.5-dev +v2022.1-dev 2022-01-26 + - General + - Add SPIR-V 1.6 support to wasm build (#4674) + - Improvements to disassembly within PassManager (#4677) + - Basic support for SPIR-V 1.6 (#4663) + - reflect debug (#4662) + - Fix endianness of string literals (#4622) + - Optimizer + - spirv-opt: add pass to Spread Volatile semantics (#4667) + - Fix constant propagation and folding of FClamp instructions (#4651) + - Manually fold floating point division by zero (#4637) + - Allow ADCE to remove dead inputs (#4629) + - Linker + - Linker improvements (#4679) + * test/linker: Code factorisation and small tweaks + * linker: Do not fail when going over limits + - Validator + - val: interface struct with builtins must be Block (#4665) + - Fuzzer + - Avoid id bound errors during opt fuzzing (#4658) + - Avoid uninitialised read when parsing hex float (#4646) v2021.4 2021-11-11 - General -- cgit v1.2.3 From b1877de5cd776117050bd42f08d04b52bce16099 Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Wed, 26 Jan 2022 14:59:42 -0500 Subject: Finalize SPIRV-Tools v2022.1 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4756035e..c6e26a74 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ Revision history for SPIRV-Tools -v2022.1-dev 2022-01-26 +v2022.1 2022-01-26 - General - Add SPIR-V 1.6 support to wasm build (#4674) - Improvements to disassembly within PassManager (#4677) -- cgit v1.2.3 From ff91b1c275543c7ba4e27060a7f209bc1ab71eb9 Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Wed, 26 Jan 2022 15:02:49 -0500 Subject: Start SPIRV-Tools v2022.2 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index c6e26a74..246e4839 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ Revision history for SPIRV-Tools +v2022.2-dev 2022-01-26 + - Start v2022.2-dev + v2022.1 2022-01-26 - General - Add SPIR-V 1.6 support to wasm build (#4674) -- cgit v1.2.3 From 65ecfd1093a9a02627fbf98bd53897aca633ae92 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Wed, 26 Jan 2022 15:13:08 -0500 Subject: Fix various source comment (doxygen) typos (#4680) Found via `codespell -q 3 -L fo,lod,parm --- include/spirv-tools/libspirv.h | 6 +++--- include/spirv-tools/libspirv.hpp | 4 ++-- include/spirv-tools/optimizer.hpp | 22 +++++++++++----------- source/binary.cpp | 2 +- source/ext_inst.h | 4 ++-- source/fuzz/fact_manager/fact_manager.h | 8 ++++---- source/fuzz/fuzzer_pass.cpp | 2 +- source/fuzz/fuzzer_pass_donate_modules.cpp | 2 +- source/fuzz/protobufs/spvtoolsfuzz.proto | 6 +++--- .../fuzz/transformation_add_constant_composite.cpp | 2 +- ...nsformation_duplicate_region_with_selection.cpp | 2 +- .../fuzz/transformation_replace_id_with_synonym.h | 2 +- source/link/CMakeLists.txt | 2 +- source/link/linker.cpp | 2 +- source/opcode.cpp | 4 ++-- source/opt/amd_ext_to_khr.h | 2 +- source/opt/basic_block.h | 2 +- source/opt/cfg.h | 2 +- source/opt/const_folding_rules.cpp | 4 ++-- source/opt/constants.h | 2 +- source/opt/convert_to_half_pass.cpp | 2 +- source/opt/dead_branch_elim_pass.h | 2 +- source/opt/debug_info_manager.cpp | 2 +- source/opt/def_use_manager.h | 4 ++-- source/opt/desc_sroa.cpp | 6 +++--- source/opt/dominator_tree.cpp | 4 ++-- source/opt/eliminate_dead_members_pass.cpp | 4 ++-- source/opt/fold.cpp | 2 +- .../fold_spec_constant_op_and_composite_pass.cpp | 2 +- source/opt/graphics_robust_access_pass.cpp | 6 +++--- source/opt/graphics_robust_access_pass.h | 2 +- source/opt/inst_bindless_check_pass.h | 4 ++-- source/opt/instruction.h | 2 +- source/opt/instrument_pass.h | 6 +++--- source/opt/ir_context.cpp | 2 +- source/opt/ir_context.h | 6 +++--- source/opt/local_access_chain_convert_pass.h | 2 +- source/opt/loop_descriptor.cpp | 6 +++--- source/opt/loop_descriptor.h | 2 +- source/opt/loop_fission.cpp | 2 +- source/opt/loop_fission.h | 2 +- source/opt/loop_fusion.cpp | 4 ++-- source/opt/loop_fusion.h | 2 +- source/opt/loop_fusion_pass.h | 2 +- source/opt/loop_peeling.h | 2 +- source/opt/loop_unroller.cpp | 10 +++++----- source/opt/loop_unswitch_pass.cpp | 2 +- source/opt/loop_unswitch_pass.h | 2 +- source/opt/loop_utils.h | 4 ++-- source/opt/merge_return_pass.h | 4 ++-- source/opt/pass.h | 2 +- source/opt/pass_manager.h | 4 ++-- source/opt/private_to_local_pass.h | 2 +- source/opt/redundancy_elimination.h | 2 +- .../replace_desc_array_access_using_var_index.h | 4 ++-- source/opt/scalar_analysis.cpp | 2 +- source/opt/scalar_analysis_nodes.h | 2 +- source/opt/scalar_analysis_simplification.cpp | 2 +- source/opt/unify_const_pass.cpp | 2 +- source/opt/vector_dce.h | 2 +- .../remove_struct_member_reduction_opportunity.cpp | 2 +- ..._struct_member_reduction_opportunity_finder.cpp | 4 ++-- ...truct_to_block_reduction_opportunity_finder.cpp | 4 ++-- source/spirv_definition.h | 2 +- source/spirv_endian.h | 2 +- source/spirv_target_env.h | 2 +- source/util/bit_vector.h | 2 +- source/util/ilist.h | 2 +- source/util/parse_number.h | 2 +- source/util/small_vector.h | 4 ++-- source/util/timer.h | 6 +++--- source/val/basic_block.h | 12 ++++++------ source/val/function.cpp | 4 ++-- source/val/function.h | 10 +++++----- source/val/validate.cpp | 2 +- source/val/validate.h | 2 +- source/val/validate_arithmetics.cpp | 2 +- source/val/validate_cfg.cpp | 4 ++-- source/val/validate_decorations.cpp | 4 ++-- source/val/validate_extensions.cpp | 2 +- source/val/validate_image.cpp | 6 +++--- source/val/validate_instruction.cpp | 4 ++-- source/val/validate_scopes.cpp | 4 ++-- source/val/validation_state.cpp | 8 ++++---- source/val/validation_state.h | 6 +++--- test/binary_parse_test.cpp | 2 +- test/c_interface_test.cpp | 2 +- test/ext_inst.cldebug100_test.cpp | 2 +- test/ext_inst.opencl_test.cpp | 2 +- test/fuzz/fuzzerutil_test.cpp | 4 ++-- test/fuzz/transformation_add_parameter_test.cpp | 2 +- test/fuzz/transformation_add_type_int_test.cpp | 2 +- .../transformation_adjust_branch_weights_test.cpp | 2 +- test/hex_float_test.cpp | 2 +- test/opt/aggressive_dead_code_elim_test.cpp | 2 +- test/opt/assembly_builder.h | 4 ++-- test/opt/block_merge_test.cpp | 2 +- test/opt/code_sink_test.cpp | 6 +++--- test/opt/dead_branch_elim_test.cpp | 2 +- test/opt/decoration_manager_test.cpp | 4 ++-- test/opt/def_use_test.cpp | 2 +- test/opt/dominator_tree/generated.cpp | 2 +- test/opt/eliminate_dead_const_test.cpp | 2 +- test/opt/flatten_decoration_test.cpp | 2 +- test/opt/fold_test.cpp | 2 +- test/opt/graphics_robust_access_test.cpp | 2 +- test/opt/inline_test.cpp | 2 +- test/opt/inst_bindless_check_test.cpp | 4 ++-- test/opt/inst_debug_printf_test.cpp | 4 ++-- test/opt/loop_optimizations/loop_descriptions.cpp | 2 +- test/opt/loop_optimizations/loop_fission.cpp | 2 +- test/opt/loop_optimizations/unroll_simple.cpp | 4 ++-- test/opt/module_test.cpp | 2 +- test/opt/pass_manager_test.cpp | 2 +- test/opt/set_spec_const_default_value_test.cpp | 2 +- test/opt/unify_const_test.cpp | 6 +++--- test/text_to_binary.control_flow_test.cpp | 2 +- test/text_to_binary.extension_test.cpp | 2 +- test/text_to_binary_test.cpp | 2 +- test/val/val_arithmetics_test.cpp | 2 +- test/val/val_atomics_test.cpp | 4 ++-- test/val/val_cfg_test.cpp | 10 +++++----- test/val/val_id_test.cpp | 4 ++-- test/val/val_image_test.cpp | 4 ++-- test/val/val_limits_test.cpp | 2 +- test/val/val_literals_test.cpp | 2 +- test/val/val_storage_test.cpp | 2 +- tools/cfg/bin_to_dot.cpp | 4 ++-- tools/cfg/bin_to_dot.h | 2 +- tools/opt/opt.cpp | 2 +- tools/sva/README.md | 2 +- utils/generate_grammar_tables.py | 2 +- utils/git-sync-deps | 2 +- utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go | 2 +- utils/vscode/src/lsp/jsonrpc2/wire.go | 2 +- utils/vscode/src/lsp/protocol/tsprotocol.go | 16 ++++++++-------- utils/vscode/src/parser/parser.go | 2 +- 137 files changed, 232 insertions(+), 232 deletions(-) diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 53dde950..e1b8890e 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -560,7 +560,7 @@ SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver, // Creates a context object for most of the SPIRV-Tools API. // Returns null if env is invalid. // -// See specific API calls for how the target environment is interpeted +// See specific API calls for how the target environment is interpreted // (particularly assembly and validation). SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); @@ -612,7 +612,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer( // set that option. // 2) Pointers that are pass as parameters to function calls do not have to // match the storage class of the formal parameter. -// 3) Pointers that are actaul parameters on function calls do not have to point +// 3) Pointers that are actual parameters on function calls do not have to point // to the same type pointed as the formal parameter. The types just need to // logically match. // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant @@ -638,7 +638,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout( // Records whether the validator should use "scalar" block layout rules. // Scalar layout rules are more permissive than relaxed block layout. // -// See Vulkan extnesion VK_EXT_scalar_block_layout. The scalar alignment is +// See Vulkan extension VK_EXT_scalar_block_layout. The scalar alignment is // defined as follows: // - scalar alignment of a scalar is the scalar size // - scalar alignment of a vector is the scalar alignment of its component diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index 8dfb46b7..25eb8a1d 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -36,7 +36,7 @@ class Context { public: // Constructs a context targeting the given environment |env|. // - // See specific API calls for how the target environment is interpeted + // See specific API calls for how the target environment is interpreted // (particularly assembly and validation). // // The constructed instance will have an empty message consumer, which just @@ -139,7 +139,7 @@ class ValidatorOptions { // set that option. // 2) Pointers that are pass as parameters to function calls do not have to // match the storage class of the formal parameter. - // 3) Pointers that are actaul parameters on function calls do not have to + // 3) Pointers that are actual parameters on function calls do not have to // point to the same type pointed as the formal parameter. The types just // need to logically match. // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 3e63037c..0b29142e 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -43,7 +43,7 @@ class Optimizer { // consumed by the RegisterPass() method. Tokens are one-time objects that // only support move; copying is not allowed. struct PassToken { - struct Impl; // Opaque struct for holding inernal data. + struct Impl; // Opaque struct for holding internal data. PassToken(std::unique_ptr); @@ -296,11 +296,11 @@ Optimizer::PassToken CreateFreezeSpecConstantValuePass(); // and can be changed in future. A spec constant is foldable if all of its // value(s) can be determined from the module. E.g., an integer spec constant // defined with OpSpecConstantOp instruction can be folded if its value won't -// change later. This pass will replace the original OpSpecContantOp instruction -// with an OpConstant instruction. When folding composite spec constants, -// new instructions may be inserted to define the components of the composite -// constant first, then the original spec constants will be replaced by -// OpConstantComposite instructions. +// change later. This pass will replace the original OpSpecConstantOp +// instruction with an OpConstant instruction. When folding composite spec +// constants, new instructions may be inserted to define the components of the +// composite constant first, then the original spec constants will be replaced +// by OpConstantComposite instructions. // // There are some operations not supported yet: // OpSConvert, OpFConvert, OpQuantizeToF16 and @@ -326,7 +326,7 @@ Optimizer::PassToken CreateUnifyConstantPass(); // Creates a eliminate-dead-constant pass. // A eliminate-dead-constant pass removes dead constants, including normal -// contants defined by OpConstant, OpConstantComposite, OpConstantTrue, or +// constants defined by OpConstant, OpConstantComposite, OpConstantTrue, or // OpConstantFalse and spec constants defined by OpSpecConstant, // OpSpecConstantComposite, OpSpecConstantTrue, OpSpecConstantFalse or // OpSpecConstantOp. @@ -390,7 +390,7 @@ Optimizer::PassToken CreateInlineOpaquePass(); // Only modules with relaxed logical addressing (see opt/instruction.h) are // currently processed. // -// This pass is most effective if preceeded by Inlining and +// This pass is most effective if preceded by Inlining and // LocalAccessChainConvert. This pass will reduce the work needed to be done // by LocalSingleStoreElim and LocalMultiStoreElim. // @@ -408,7 +408,7 @@ Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass(); // Note that some branches and blocks may be left to avoid creating invalid // control flow. Improving this is left to future work. // -// This pass is most effective when preceeded by passes which eliminate +// This pass is most effective when preceded by passes which eliminate // local loads and stores, effectively propagating constant values where // possible. Optimizer::PassToken CreateDeadBranchElimPass(); @@ -425,7 +425,7 @@ Optimizer::PassToken CreateDeadBranchElimPass(); // are currently processed. Currently modules with any extensions enabled are // not processed. This is left for future work. // -// This pass is most effective if preceeded by Inlining and +// This pass is most effective if preceded by Inlining and // LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim // will reduce the work that this pass has to do. Optimizer::PassToken CreateLocalMultiStoreElimPass(); @@ -630,7 +630,7 @@ Optimizer::PassToken CreateRedundancyEliminationPass(); Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 100); // Create a private to local pass. -// This pass looks for variables delcared in the private storage class that are +// This pass looks for variables declared in the private storage class that are // used in only one function. Those variables are moved to the function storage // class in the function that they are used. Optimizer::PassToken CreatePrivateToLocalPass(); diff --git a/source/binary.cpp b/source/binary.cpp index bbaa2c2c..24d32f8c 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -215,7 +215,7 @@ class Parser { size_t word_index; // The current position in words. size_t instruction_count; // The count of processed instructions spv_endianness_t endian; // The endianness of the binary. - // Is the SPIR-V binary in a different endiannes from the host native + // Is the SPIR-V binary in a different endianness from the host native // endianness? bool requires_endian_conversion; diff --git a/source/ext_inst.h b/source/ext_inst.h index aff6e308..4027f4c3 100644 --- a/source/ext_inst.h +++ b/source/ext_inst.h @@ -27,7 +27,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type); // Returns true if the extended instruction set is debug info bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type); -// Finds the named extented instruction of the given type in the given extended +// Finds the named extended instruction of the given type in the given extended // instruction table. On success, returns SPV_SUCCESS and writes a handle of // the instruction entry into *entry. spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, @@ -35,7 +35,7 @@ spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, const char* name, spv_ext_inst_desc* entry); -// Finds the extented instruction of the given type in the given extended +// Finds the extended instruction of the given type in the given extended // instruction table by value. On success, returns SPV_SUCCESS and writes a // handle of the instruction entry into *entry. spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h index 5cf5b18b..ce28ae4c 100644 --- a/source/fuzz/fact_manager/fact_manager.h +++ b/source/fuzz/fact_manager/fact_manager.h @@ -163,7 +163,7 @@ class FactManager { std::vector GetSynonymsForDataDescriptor( const protobufs::DataDescriptor& data_descriptor) const; - // Returns true if and ony if |data_descriptor1| and |data_descriptor2| are + // Returns true if and only if |data_descriptor1| and |data_descriptor2| are // known to be synonymous. bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, const protobufs::DataDescriptor& data_descriptor2) const; @@ -174,7 +174,7 @@ class FactManager { //============================== // Querying facts about dead blocks - // Returns true if and ony if |block_id| is the id of a block known to be + // Returns true if and only if |block_id| is the id of a block known to be // dynamically unreachable. bool BlockIsDead(uint32_t block_id) const; @@ -184,7 +184,7 @@ class FactManager { //============================== // Querying facts about livesafe function - // Returns true if and ony if |function_id| is the id of a function known + // Returns true if and only if |function_id| is the id of a function known // to be livesafe. bool FunctionIsLivesafe(uint32_t function_id) const; @@ -194,7 +194,7 @@ class FactManager { //============================== // Querying facts about irrelevant values - // Returns true if and ony if the value of the pointee associated with + // Returns true if and only if the value of the pointee associated with // |pointer_id| is irrelevant. bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index d91482cc..6a879851 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -261,7 +261,7 @@ uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { uint32_t FuzzerPass::FindOrCreateFunctionType( uint32_t return_type_id, const std::vector& argument_id) { - // FindFunctionType has a sigle argument for OpTypeFunction operands + // FindFunctionType has a single 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; diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 5bdf6973..29ede58b 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -479,7 +479,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue( "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 + // this to the module using remapped versions of all constituent ids and // the result type. new_result_id = GetFuzzerContext()->GetFreshId(); std::vector constituent_ids; diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index cba9397b..e71b6a3f 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -1961,10 +1961,10 @@ message TransformationReplaceBooleanConstantWithConstantBinary { // A descriptor for the boolean constant id we would like to replace IdUseDescriptor id_use_descriptor = 1; - // Id for the constant to be used on the LHS of the comparision + // Id for the constant to be used on the LHS of the comparison uint32 lhs_id = 2; - // Id for the constant to be used on the RHS of the comparision + // Id for the constant to be used on the RHS of the comparison uint32 rhs_id = 3; // Opcode for binary operator @@ -2403,7 +2403,7 @@ message TransformationWrapVectorSynonym { // va = vector(..., a, ...) // vb = vector(..., b, ...) // - // where a and b are in the same position i in each of their correponding vector + // where a and b are in the same position i in each of their corresponding vector // and a is synonymous with va[i] and b is synonymous with vb[i]. // // The transformation then add an instruction vc = va op vb where c is synonymous diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp index e6cd5a96..89007ab6 100644 --- a/source/fuzz/transformation_add_constant_composite.cpp +++ b/source/fuzz/transformation_add_constant_composite.cpp @@ -75,7 +75,7 @@ bool TransformationAddConstantComposite::IsApplicable( // We do not create constants of structs decorated with Block nor // BufferBlock. The SPIR-V spec does not explicitly disallow this, but it // seems like a strange thing to do, so we disallow it to avoid triggering - // low priorty edge case issues related to it. + // low priority edge case issues related to it. if (fuzzerutil::HasBlockOrBufferBlockDecoration( ir_context, composite_type_instruction->result_id())) { return false; diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp index a80becd9..db88610f 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.cpp +++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -325,7 +325,7 @@ void TransformationDuplicateRegionWithSelection::Apply( std::map original_id_to_phi_id = fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id()); - // Use oveflow ids to fill in any required ids that are missing from these + // Use overflow ids to fill in any required ids that are missing from these // maps. for (auto block : region_blocks) { if (original_label_to_duplicate_label.count(block->id()) == 0) { diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h index 4570fcef..66f8e439 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.h +++ b/source/fuzz/transformation_replace_id_with_synonym.h @@ -32,7 +32,7 @@ class TransformationReplaceIdWithSynonym : public Transformation { protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id); // - The fact manager must know that the id identified by - // |message_.id_use_descriptor| is synonomous with |message_.synonymous_id|. + // |message_.id_use_descriptor| is synonymous with |message_.synonymous_id|. // - Replacing the id in |message_.id_use_descriptor| by // |message_.synonymous_id| must respect SPIR-V's rules about uses being // dominated by their definitions. diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt index c8dd2f71..a452a107 100644 --- a/source/link/CMakeLists.txt +++ b/source/link/CMakeLists.txt @@ -23,7 +23,7 @@ target_include_directories(SPIRV-Tools-link $ PRIVATE ${spirv-tools_BINARY_DIR} ) -# We need the IR functionnalities from the optimizer +# We need the IR functionalities from the optimizer target_link_libraries(SPIRV-Tools-link PUBLIC SPIRV-Tools-opt) diff --git a/source/link/linker.cpp b/source/link/linker.cpp index c23a23de..76ce775d 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -128,7 +128,7 @@ spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer, // Remove linkage specific instructions, such as prototypes of imported // functions, declarations of imported variables, import (and export if -// necessary) linkage attribtes. +// necessary) linkage attributes. // // |linked_context| and |decoration_manager| should not be null, and the // 'RemoveDuplicatePass' should be run first. diff --git a/source/opcode.cpp b/source/opcode.cpp index c96cde8d..88085df7 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -40,12 +40,12 @@ struct OpcodeDescPtrLen { static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries), kOpcodeTableEntries}; -// Represents a vendor tool entry in the SPIR-V XML Regsitry. +// Represents a vendor tool entry in the SPIR-V XML Registry. struct VendorTool { uint32_t value; const char* vendor; const char* tool; // Might be empty string. - const char* vendor_tool; // Combiantion of vendor and tool. + const char* vendor_tool; // Combination of vendor and tool. }; const VendorTool vendor_tools[] = { diff --git a/source/opt/amd_ext_to_khr.h b/source/opt/amd_ext_to_khr.h index fd3dab4e..6a39d953 100644 --- a/source/opt/amd_ext_to_khr.h +++ b/source/opt/amd_ext_to_khr.h @@ -23,7 +23,7 @@ namespace spvtools { namespace opt { // Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, and -// VK_AMD_shader_trinary_minmax with equivalant code using core instructions and +// VK_AMD_shader_trinary_minmax with equivalent code using core instructions and // capabilities. class AmdExtensionToKhrPass : public Pass { public: diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h index 6741a50f..dd3b2e28 100644 --- a/source/opt/basic_block.h +++ b/source/opt/basic_block.h @@ -83,7 +83,7 @@ class BasicBlock { const Instruction* GetMergeInst() const; Instruction* GetMergeInst(); - // Returns the OpLoopMerge instruciton in this basic block, if it exists. + // Returns the OpLoopMerge instruction in this basic block, if it exists. // Otherwise return null. May be used whenever tail() can be used. const Instruction* GetLoopMergeInst() const; Instruction* GetLoopMergeInst(); diff --git a/source/opt/cfg.h b/source/opt/cfg.h index f2806822..33412f18 100644 --- a/source/opt/cfg.h +++ b/source/opt/cfg.h @@ -30,7 +30,7 @@ class CFG { public: explicit CFG(Module* module); - // Return the list of predecesors for basic block with label |blkid|. + // Return the list of predecessors for basic block with label |blkid|. // TODO(dnovillo): Move this to BasicBlock. const std::vector& preds(uint32_t blk_id) const { assert(label2preds_.count(blk_id)); diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index a9830a25..249e11e5 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp @@ -550,7 +550,7 @@ ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); } ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); } // Returns the constant that results from evaluating |numerator| / 0.0. Returns -// |nullptr| if the result could not be evalutated. +// |nullptr| if the result could not be evaluated. const analysis::Constant* FoldFPScalarDivideByZero( const analysis::Type* result_type, const analysis::Constant* numerator, analysis::ConstantManager* const_mgr) { @@ -1346,7 +1346,7 @@ void ConstantFoldingRules::AddFoldingRules() { FoldFPUnaryOp(FoldFTranscendentalUnary(std::log))); #ifdef __ANDROID__ - // Android NDK r15c tageting ABI 15 doesn't have full support for C++11 + // Android NDK r15c targeting 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); }; diff --git a/source/opt/constants.h b/source/opt/constants.h index 10f7bd68..c039ae08 100644 --- a/source/opt/constants.h +++ b/source/opt/constants.h @@ -541,7 +541,7 @@ class ConstantManager { // instruction at the end of the current module's types section. // // |type_id| is an optional argument for disambiguating equivalent types. If - // |type_id| is specified, the contant returned will have that type id. + // |type_id| is specified, the constant returned will have that type id. Instruction* GetDefiningInstruction(const Constant* c, uint32_t type_id = 0, Module::inst_iterator* pos = nullptr); diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index b127eabe..4086e31a 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp @@ -181,7 +181,7 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width, uint32_t to_width) { // Add converts of any float operands to to_width if they are of from_width. // If converting to 16, change type of phi to float16 equivalent and remember - // result id. Converts need to be added to preceeding blocks. + // result id. Converts need to be added to preceding blocks. uint32_t ocnt = 0; uint32_t* prev_idp; bool modified = false; diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h index c350bb2f..198bad2d 100644 --- a/source/opt/dead_branch_elim_pass.h +++ b/source/opt/dead_branch_elim_pass.h @@ -158,7 +158,7 @@ class DeadBranchElimPass : public MemPass { uint32_t cont_id, uint32_t header_id, uint32_t merge_id, std::unordered_set* blocks_with_back_edges); - // Returns true if there is a brach to the merge node of the selection + // Returns true if there is a branch to the merge node of the selection // construct |switch_header_id| that is inside a nested selection construct or // in the header of the nested selection construct. bool SwitchHasNestedBreak(uint32_t switch_header_id); diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index 060e0d93..c1df6258 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -149,7 +149,7 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, // Create new constant directly into global value area, bypassing the // Constant manager. This is used when the DefUse or Constant managers // are invalid and cannot be regenerated due to the module being in an -// inconsistant state e.g. in the middle of significant modification +// inconsistent state e.g. in the middle of significant modification // such as inlining. Invalidate Constant and DefUse managers if used. uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) { uint32_t id = context->TakeNextId(); diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h index d66575d3..a8dbbc60 100644 --- a/source/opt/def_use_manager.h +++ b/source/opt/def_use_manager.h @@ -190,7 +190,7 @@ class DefUseManager { // Returns the annotation instrunctions which are a direct use of the given // |id|. This means when the decorations are applied through decoration // group(s), this function will just return the OpGroupDecorate - // instrcution(s) which refer to the given id as an operand. The OpDecorate + // instruction(s) which refer to the given id as an operand. The OpDecorate // instructions which decorate the decoration group will not be returned. std::vector GetAnnotations(uint32_t id) const; @@ -210,7 +210,7 @@ class DefUseManager { friend bool CompareAndPrintDifferences(const DefUseManager&, const DefUseManager&); - // If |inst| has not already been analysed, then analyses its defintion and + // If |inst| has not already been analysed, then analyses its definition and // uses. void UpdateDefUse(Instruction* inst); diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index 7263c120..b130ca80 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp @@ -118,7 +118,7 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, if (use->NumInOperands() == 2) { // We are not indexing into the replacement variable. We can replaces the - // access chain with the replacement varibale itself. + // access chain with the replacement variable itself. context()->ReplaceAllUsesWith(use->result_id(), replacement_var); context()->KillInst(use); return true; @@ -135,8 +135,8 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, // Use the replacement variable as the base address. new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var}}); - // Drop the first index because it is consumed by the replacment, and copy the - // rest. + // Drop the first index because it is consumed by the replacement, and copy + // the rest. for (uint32_t i = 4; i < use->NumOperands(); i++) { new_operands.emplace_back(use->GetOperand(i)); } diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp index 55287f44..d86de151 100644 --- a/source/opt/dominator_tree.cpp +++ b/source/opt/dominator_tree.cpp @@ -48,7 +48,7 @@ namespace { // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode // SuccessorLambda - Lamdba matching the signature of 'const // std::vector*(const BBType *A)'. Will return a vector of the nodes -// succeding BasicBlock A. +// succeeding BasicBlock A. // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be // called on each node traversed AFTER their children. // PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be @@ -69,7 +69,7 @@ static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode // SuccessorLambda - Lamdba matching the signature of 'const // std::vector*(const BBType *A)'. Will return a vector of the nodes -// succeding BasicBlock A. +// succeeding BasicBlock A. // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be // called on each node traversed after their children. template diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp index a24ba8f4..52aca525 100644 --- a/source/opt/eliminate_dead_members_pass.cpp +++ b/source/opt/eliminate_dead_members_pass.cpp @@ -38,7 +38,7 @@ Pass::Status EliminateDeadMembersPass::Process() { } void EliminateDeadMembersPass::FindLiveMembers() { - // Until we have implemented the rewritting of OpSpecConsantOp instructions, + // Until we have implemented the rewriting of OpSpecConsantOp instructions, // we have to mark them as fully used just to be safe. for (auto& inst : get_module()->types_values()) { if (inst.opcode() == SpvOpSpecConstantOp) { @@ -570,7 +570,7 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case SpvOpTypeStruct: - // The type will have already been rewriten, so use the new member + // The type will have already been rewritten, so use the new member // index. type_id = type_inst->GetSingleWordInOperand(new_member_idx); break; diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp index 6550fb4f..b903da6a 100644 --- a/source/opt/fold.cpp +++ b/source/opt/fold.cpp @@ -540,7 +540,7 @@ std::vector InstructionFolder::FoldVectors( // in 32-bit words here. The reason of not using FoldScalars() here // is that we do not create temporary null constants as components // when the vector operand is a NullConstant because Constant creation - // may need extra checks for the validity and that is not manageed in + // may need extra checks for the validity and that is not managed in // here. if (const analysis::ScalarConstant* scalar_component = vector_operand->GetComponents().at(d)->AsScalarConstant()) { diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp index 8ab717ea..85f11fde 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp +++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp @@ -115,7 +115,7 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( Instruction* folded_inst = nullptr; assert(inst->GetInOperand(0).type == SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER && - "The first in-operand of OpSpecContantOp instruction must be of " + "The first in-operand of OpSpecConstantOp instruction must be of " "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type"); switch (static_cast(inst->GetSingleWordInOperand(0))) { diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index 336dcd83..4652d72d 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp @@ -13,7 +13,7 @@ // limitations under the License. // This pass injects code in a graphics shader to implement guarantees -// satisfying Vulkan's robustBufferAcces rules. Robust access rules permit +// satisfying Vulkan's robustBufferAccess rules. Robust access rules permit // an out-of-bounds access to be redirected to an access of the same type // (load, store, etc.) but within the same root object. // @@ -74,7 +74,7 @@ // Pointers are always (correctly) typed and so the address and number of // consecutive locations are fully determined by the pointer. // -// - A pointer value orginates as one of few cases: +// - A pointer value originates as one of few cases: // // - OpVariable for an interface object or an array of them: image, // buffer (UBO or SSBO), sampler, sampled-image, push-constant, input @@ -958,7 +958,7 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( constant_mgr->GetDefiningInstruction(component_0)->result_id(); // If the image is a cube array, then the last component of the queried - // size is the layer count. In the query, we have to accomodate folding + // size is the layer count. In the query, we have to accommodate folding // in the face index ranging from 0 through 5. The inclusive upper bound // on the third coordinate therefore is multiplied by 6. auto* query_size_including_faces = query_size; diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h index 6fc692c1..8f4c9dc7 100644 --- a/source/opt/graphics_robust_access_pass.h +++ b/source/opt/graphics_robust_access_pass.h @@ -111,7 +111,7 @@ class GraphicsRobustAccessPass : public Pass { Instruction* max, Instruction* where); // Returns a new instruction which evaluates to the length the runtime array - // referenced by the access chain at the specfied index. The instruction is + // referenced by the access chain at the specified index. The instruction is // inserted before the access chain instruction. Returns a null pointer in // some cases if assumptions are violated (rather than asserting out). opt::Instruction* MakeRuntimeArrayLengthInst(Instruction* access_chain, diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h index cd961805..e6e6ef4f 100644 --- a/source/opt/inst_bindless_check_pass.h +++ b/source/opt/inst_bindless_check_pass.h @@ -147,11 +147,11 @@ class InstBindlessCheckPass : public InstrumentPass { uint32_t GenLastByteIdx(RefAnalysis* ref, InstructionBuilder* builder); // Clone original image computation starting at |image_id| into |builder|. - // This may generate more than one instruction if neccessary. + // This may generate more than one instruction if necessary. uint32_t CloneOriginalImage(uint32_t image_id, InstructionBuilder* builder); // Clone original original reference encapsulated by |ref| into |builder|. - // This may generate more than one instruction if neccessary. + // This may generate more than one instruction if necessary. uint32_t CloneOriginalReference(RefAnalysis* ref, InstructionBuilder* builder); diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 57ee7073..f87f563a 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -122,7 +122,7 @@ inline bool operator!=(const Operand& o1, const Operand& o2) { } // This structure is used to represent a DebugScope instruction from -// the OpenCL.100.DebugInfo extened instruction set. Note that we can +// the OpenCL.100.DebugInfo extended 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. diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h index 12b939d4..90c1dd47 100644 --- a/source/opt/instrument_pass.h +++ b/source/opt/instrument_pass.h @@ -50,7 +50,7 @@ // A validation pass may read or write multiple buffers. All such buffers // are located in a single debug descriptor set whose index is passed at the // creation of the instrumentation pass. The bindings of the buffers used by -// a validation pass are permanantly assigned and fixed and documented by +// a validation pass are permanently assigned and fixed and documented by // the kDebugOutput* static consts. namespace spvtools { @@ -179,8 +179,8 @@ class InstrumentPass : public Pass { // the error. Every stage will write a fixed number of words. Vertex shaders // will write the Vertex and Instance ID. Fragment shaders will write // FragCoord.xy. Compute shaders will write the GlobalInvocation ID. - // The tesselation eval shader will write the Primitive ID and TessCoords.uv. - // The tesselation control shader and geometry shader will write the + // The tessellation eval shader will write the Primitive ID and TessCoords.uv. + // The tessellation control shader and geometry shader will write the // Primitive ID and Invocation ID. // // The Validation Error Code specifies the exact error which has occurred. diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 78918461..5b0beeb2 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -106,7 +106,7 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { analyses_to_invalidate |= kAnalysisDebugInfo; } - // The dominator analysis hold the psuedo entry and exit nodes from the CFG. + // The dominator analysis hold the pseudo entry and exit nodes from the CFG. // Also if the CFG change the dominators many changed as well, so the // dominator analysis should be invalidated as well. if (analyses_to_invalidate & kAnalysisCFG) { diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 7bef3054..274dd14e 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -302,7 +302,7 @@ class IRContext { } } - // Returns a pointer the decoration manager. If the decoration manger is + // Returns a pointer the decoration manager. If the decoration manager is // invalid, it is rebuilt first. analysis::DecorationManager* get_decoration_mgr() { if (!AreAnalysesValid(kAnalysisDecorations)) { @@ -385,7 +385,7 @@ class IRContext { // Deletes the instruction defining the given |id|. Returns true on // success, false if the given |id| is not defined at all. This method also - // erases the name, decorations, and defintion of |id|. + // erases the name, decorations, and definition of |id|. // // Pointers and iterators pointing to the deleted instructions become invalid. // However other pointers and iterators are still valid. @@ -802,7 +802,7 @@ class IRContext { // iterators to traverse instructions. std::unordered_map id_to_func_; - // A bitset indicating which analyes are currently valid. + // A bitset indicating which analyzes are currently valid. Analysis valid_analyses_; // Opcodes of shader capability core executable instructions diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h index 552062e5..a51660f1 100644 --- a/source/opt/local_access_chain_convert_pass.h +++ b/source/opt/local_access_chain_convert_pass.h @@ -81,7 +81,7 @@ class LocalAccessChainConvertPass : public MemPass { std::vector* in_opnds); // Create a load/insert/store equivalent to a store of - // |valId| through (constant index) access chaing |ptrInst|. + // |valId| through (constant index) access chain |ptrInst|. // Append to |newInsts|. Returns true if successful. bool GenAccessChainStoreReplacement( const Instruction* ptrInst, uint32_t valId, diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp index b5b56309..9bc495e5 100644 --- a/source/opt/loop_descriptor.cpp +++ b/source/opt/loop_descriptor.cpp @@ -719,7 +719,7 @@ bool Loop::FindNumberOfIterations(const Instruction* induction, step_value = -step_value; } - // Find the inital value of the loop and make sure it is a constant integer. + // Find the initial value of the loop and make sure it is a constant integer. int64_t init_value = 0; if (!GetInductionInitValue(induction, &init_value)) return false; @@ -751,7 +751,7 @@ bool Loop::FindNumberOfIterations(const Instruction* induction, // We retrieve the number of iterations using the following formula, diff / // |step_value| where diff is calculated differently according to the // |condition| and uses the |condition_value| and |init_value|. If diff / -// |step_value| is NOT cleanly divisable then we add one to the sum. +// |step_value| is NOT cleanly divisible then we add one to the sum. int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value, int64_t init_value, int64_t step_value) const { int64_t diff = 0; @@ -795,7 +795,7 @@ int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value, // If the condition is not met to begin with the loop will never iterate. if (!(init_value >= condition_value)) return 0; - // We subract one to make it the same as SpvOpGreaterThan as it is + // We subtract one to make it the same as SpvOpGreaterThan as it is // functionally equivalent. diff = init_value - (condition_value - 1); diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h index 4b4f8bc7..e88ff936 100644 --- a/source/opt/loop_descriptor.h +++ b/source/opt/loop_descriptor.h @@ -395,7 +395,7 @@ class Loop { // Sets |merge| as the loop merge block. No checks are performed here. inline void SetMergeBlockImpl(BasicBlock* merge) { loop_merge_ = merge; } - // Each differnt loop |condition| affects how we calculate the number of + // Each different loop |condition| affects how we calculate the number of // iterations using the |condition_value|, |init_value|, and |step_values| of // the induction variable. This method will return the number of iterations in // a loop with those values for a given |condition|. diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp index 0a4125df..b4df8c62 100644 --- a/source/opt/loop_fission.cpp +++ b/source/opt/loop_fission.cpp @@ -29,7 +29,7 @@ // 2 - For each loop in the list, group each instruction into a set of related // instructions by traversing each instructions users and operands recursively. // We stop if we encounter an instruction we have seen before or an instruction -// which we don't consider relevent (i.e OpLoopMerge). We then group these +// which we don't consider relevant (i.e OpLoopMerge). We then group these // groups into two different sets, one for the first loop and one for the // second. // diff --git a/source/opt/loop_fission.h b/source/opt/loop_fission.h index e7a59c18..9bc12c0f 100644 --- a/source/opt/loop_fission.h +++ b/source/opt/loop_fission.h @@ -33,7 +33,7 @@ namespace opt { class LoopFissionPass : public Pass { public: - // Fuction used to determine if a given loop should be split. Takes register + // Function used to determine if a given loop should be split. Takes register // pressure region for that loop as a parameter and returns true if the loop // should be split. using FissionCriteriaFunction = diff --git a/source/opt/loop_fusion.cpp b/source/opt/loop_fusion.cpp index 07d171a0..f3aab283 100644 --- a/source/opt/loop_fusion.cpp +++ b/source/opt/loop_fusion.cpp @@ -165,7 +165,7 @@ bool LoopFusion::AreCompatible() { // Check adjacency, |loop_0_| should come just before |loop_1_|. // There is always at least one block between loops, even if it's empty. - // We'll check at most 2 preceeding blocks. + // We'll check at most 2 preceding blocks. auto pre_header_1 = loop_1_->GetPreHeaderBlock(); @@ -712,7 +712,7 @@ void LoopFusion::Fuse() { ld->RemoveLoop(loop_1_); - // Kill unnessecary instructions and remove all empty blocks. + // Kill unnecessary instructions and remove all empty blocks. for (auto inst : instr_to_delete) { context_->KillInst(inst); } diff --git a/source/opt/loop_fusion.h b/source/opt/loop_fusion.h index d61d6783..769da5f1 100644 --- a/source/opt/loop_fusion.h +++ b/source/opt/loop_fusion.h @@ -40,7 +40,7 @@ class LoopFusion { // That means: // * they both have one induction variable // * they have the same upper and lower bounds - // - same inital value + // - same initial value // - same condition // * they have the same update step // * they are adjacent, with |loop_0| appearing before |loop_1| diff --git a/source/opt/loop_fusion_pass.h b/source/opt/loop_fusion_pass.h index 3a0be600..9d5b7ccd 100644 --- a/source/opt/loop_fusion_pass.h +++ b/source/opt/loop_fusion_pass.h @@ -33,7 +33,7 @@ class LoopFusionPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Status Process() override; private: diff --git a/source/opt/loop_peeling.h b/source/opt/loop_peeling.h index 413f896f..2a55fe44 100644 --- a/source/opt/loop_peeling.h +++ b/source/opt/loop_peeling.h @@ -261,7 +261,7 @@ class LoopPeelingPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Pass::Status Process() override; private: diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index aff191fe..28ff0729 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp @@ -163,7 +163,7 @@ struct LoopUnrollState { }; // This class implements the actual unrolling. It uses a LoopUnrollState to -// maintain the state of the unrolling inbetween steps. +// maintain the state of the unrolling in between steps. class LoopUnrollerUtilsImpl { public: using BasicBlockListTy = std::vector>; @@ -209,7 +209,7 @@ class LoopUnrollerUtilsImpl { // Add all blocks_to_add_ to function_ at the |insert_point|. void AddBlocksToFunction(const BasicBlock* insert_point); - // Duplicates the |old_loop|, cloning each body and remaping the ids without + // Duplicates the |old_loop|, cloning each body and remapping the ids without // removing instructions or changing relative structure. Result will be stored // in |new_loop|. void DuplicateLoop(Loop* old_loop, Loop* new_loop); @@ -241,7 +241,7 @@ class LoopUnrollerUtilsImpl { // Remap all the in |basic_block| to new IDs and keep the mapping of new ids // to old // ids. |loop| is used to identify special loop blocks (header, continue, - // ect). + // etc). void AssignNewResultIds(BasicBlock* basic_block); // Using the map built by AssignNewResultIds, replace the uses in |inst| @@ -320,7 +320,7 @@ class LoopUnrollerUtilsImpl { // and then be remapped at the end. std::vector loop_phi_instructions_; - // The number of loop iterations that the loop would preform pre-unroll. + // The number of loop iterations that the loop would perform pre-unroll. size_t number_of_loop_iterations_; // The amount that the loop steps each iteration. @@ -839,7 +839,7 @@ void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) { new_loop->SetMergeBlock(new_merge); } -// Whenever the utility copies a block it stores it in a tempory buffer, this +// Whenever the utility copies a block it stores it in a temporary buffer, this // function adds the buffer into the Function. The blocks will be inserted // after the block |insert_point|. void LoopUnrollerUtilsImpl::AddBlocksToFunction( diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp index d805ecf3..1ee7e5e2 100644 --- a/source/opt/loop_unswitch_pass.cpp +++ b/source/opt/loop_unswitch_pass.cpp @@ -118,7 +118,7 @@ class LoopUnswitch { // Find a value that can be used to select the default path. // If none are possible, then it will just use 0. The value does not matter - // because this path will never be taken becaues the new switch outside of + // because this path will never be taken because the new switch outside of // the loop cannot select this path either. std::vector existing_values; for (uint32_t i = 2; i < switch_inst->NumInOperands(); i += 2) { diff --git a/source/opt/loop_unswitch_pass.h b/source/opt/loop_unswitch_pass.h index 3ecdd611..4f7295d4 100644 --- a/source/opt/loop_unswitch_pass.h +++ b/source/opt/loop_unswitch_pass.h @@ -30,7 +30,7 @@ class LoopUnswitchPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Pass::Status Process() override; private: diff --git a/source/opt/loop_utils.h b/source/opt/loop_utils.h index a4e61900..70060fc4 100644 --- a/source/opt/loop_utils.h +++ b/source/opt/loop_utils.h @@ -123,7 +123,7 @@ class LoopUtils { // Clone the |loop_| and make the new loop branch to the second loop on exit. Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); - // Perfom a partial unroll of |loop| by given |factor|. This will copy the + // Perform a partial unroll of |loop| by given |factor|. This will copy the // body of the loop |factor| times. So a |factor| of one would give a new loop // with the original body plus one unrolled copy body. bool PartiallyUnroll(size_t factor); @@ -139,7 +139,7 @@ class LoopUtils { // 1. That the loop is in structured order. // 2. That the continue block is a branch to the header. // 3. That the only phi used in the loop is the induction variable. - // TODO(stephen@codeplay.com): This is a temporary mesure, after the loop is + // TODO(stephen@codeplay.com): This is a temporary measure, after the loop is // converted into LCSAA form and has a single entry and exit we can rewrite // the other phis. // 4. That this is an inner most loop, or that loops contained within this diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h index 4096ce7d..a35cf269 100644 --- a/source/opt/merge_return_pass.h +++ b/source/opt/merge_return_pass.h @@ -247,7 +247,7 @@ class MergeReturnPass : public MemPass { // Add new phi nodes for any id that no longer dominate all of it uses. A phi // node is added to a block |bb| for an id if the id is defined between the - // original immediate dominator of |bb| and its new immidiate dominator. It + // original immediate dominator of |bb| and its new immediate dominator. It // is assumed that at this point there are no unreachable blocks in the // control flow graph. void AddNewPhiNodes(); @@ -273,7 +273,7 @@ class MergeReturnPass : public MemPass { void InsertAfterElement(BasicBlock* element, BasicBlock* new_element, std::list* list); - // Creates a single case switch around all of the exectuable code of the + // Creates a single case switch around all of the executable code of the // current function where the switch and case value are both zero and the // default is the merge block. Returns after the switch is executed. Sets // |final_return_block_|. diff --git a/source/opt/pass.h b/source/opt/pass.h index a8c9c4b4..4a8ea674 100644 --- a/source/opt/pass.h +++ b/source/opt/pass.h @@ -129,7 +129,7 @@ class Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes are made to the module. + // successful to indicate whether changes are made to the module. virtual Status Process() = 0; // Return the next available SSA id and increment it. diff --git a/source/opt/pass_manager.h b/source/opt/pass_manager.h index 9686dddc..11961a33 100644 --- a/source/opt/pass_manager.h +++ b/source/opt/pass_manager.h @@ -54,7 +54,7 @@ class PassManager { // Adds an externally constructed pass. void AddPass(std::unique_ptr pass); // Uses the argument |args| to construct a pass instance of type |T|, and adds - // the pass instance to this pass manger. The pass added will use this pass + // the pass instance to this pass manager. The pass added will use this pass // manager's message consumer. template void AddPass(Args&&... args); @@ -70,7 +70,7 @@ class PassManager { // Runs all passes on the given |module|. Returns Status::Failure if errors // occur when processing using one of the registered passes. All passes // registered after the error-reporting pass will be skipped. Returns the - // corresponding Status::Success if processing is succesful to indicate + // corresponding Status::Success if processing is successful to indicate // whether changes are made to the module. // // After running all the passes, they are removed from the list. diff --git a/source/opt/private_to_local_pass.h b/source/opt/private_to_local_pass.h index c6127d67..e96a965e 100644 --- a/source/opt/private_to_local_pass.h +++ b/source/opt/private_to_local_pass.h @@ -44,7 +44,7 @@ class PrivateToLocalPass : public Pass { // class of |function|. Returns false if the variable could not be moved. bool MoveVariable(Instruction* variable, Function* function); - // |inst| is an instruction declaring a varible. If that variable is + // |inst| is an instruction declaring a variable. If that variable is // referenced in a single function and all of uses are valid as defined by // |IsValidUse|, then that function is returned. Otherwise, the return // value is |nullptr|. diff --git a/source/opt/redundancy_elimination.h b/source/opt/redundancy_elimination.h index 91809b5d..40451f40 100644 --- a/source/opt/redundancy_elimination.h +++ b/source/opt/redundancy_elimination.h @@ -41,7 +41,7 @@ class RedundancyEliminationPass : public LocalRedundancyEliminationPass { // in the function containing |bb|. // // |value_to_ids| is a map from value number to ids. If {vn, id} is in - // |value_to_ids| then vn is the value number of id, and the defintion of id + // |value_to_ids| then vn is the value number of id, and the definition of id // dominates |bb|. // // Returns true if at least one instruction is deleted. diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h index e18222c8..0c97f7eb 100644 --- a/source/opt/replace_desc_array_access_using_var_index.h +++ b/source/opt/replace_desc_array_access_using_var_index.h @@ -47,7 +47,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass { } private: - // Replaces all acceses to |var| using variable indices with constant + // Replaces all accesses to |var| using variable indices with constant // elements of the array |var|. Creates switch-case statements to determine // the value of the variable index for all the possible cases. Returns // whether replacement is done or not. @@ -170,7 +170,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass { // Creates and adds an OpSwitch used for the selection of OpAccessChain whose // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be // added at the end of |parent_block|. It will jump to |default_id| for the - // default case and jumps to one of case blocks whoes ids are |case_block_ids| + // default case and jumps to one of case blocks whose ids are |case_block_ids| // if |access_chain_index_var_id| matches the case number. |merge_id| is the // merge block id. void AddSwitchForAccessChain( diff --git a/source/opt/scalar_analysis.cpp b/source/opt/scalar_analysis.cpp index 38555e64..2b0a824c 100644 --- a/source/opt/scalar_analysis.cpp +++ b/source/opt/scalar_analysis.cpp @@ -581,7 +581,7 @@ static void PushToString(T id, std::u32string* str) { // Implements the hashing of SENodes. size_t SENodeHash::operator()(const SENode* node) const { - // Concatinate the terms into a string which we can hash. + // Concatenate the terms into a string which we can hash. std::u32string hash_string{}; // Hashing the type as a string is safer than hashing the enum as the enum is diff --git a/source/opt/scalar_analysis_nodes.h b/source/opt/scalar_analysis_nodes.h index b0e3fefd..91ce446f 100644 --- a/source/opt/scalar_analysis_nodes.h +++ b/source/opt/scalar_analysis_nodes.h @@ -167,7 +167,7 @@ class SENode { const ChildContainerType& GetChildren() const { return children_; } ChildContainerType& GetChildren() { return children_; } - // Return true if this node is a cant compute node. + // Return true if this node is a can't compute node. bool IsCantCompute() const { return GetType() == CanNotCompute; } // Implements a casting method for each type. diff --git a/source/opt/scalar_analysis_simplification.cpp b/source/opt/scalar_analysis_simplification.cpp index 9c81dbe9..3c1ecc08 100644 --- a/source/opt/scalar_analysis_simplification.cpp +++ b/source/opt/scalar_analysis_simplification.cpp @@ -88,7 +88,7 @@ class SENodeSimplifyImpl { private: // Recursively descend through the graph to build up the accumulator objects - // which are used to flatten the graph. |child| is the node currenty being + // which are used to flatten the graph. |child| is the node currently being // traversed and the |negation| flag is used to signify that this operation // was preceded by a unary negative operation and as such the result should be // negated. diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp index 227fd61d..6bfa11a5 100644 --- a/source/opt/unify_const_pass.cpp +++ b/source/opt/unify_const_pass.cpp @@ -151,7 +151,7 @@ Pass::Status UnifyConstantPass::Process() { // 'SpecId' decoration and all of them should be treated as unique. // 'SpecId' is not applicable to SpecConstants defined with // OpSpecConstant{Op|Composite}, their values are not necessary to be - // unique. When all the operands/compoents are the same between two + // unique. When all the operands/components are the same between two // OpSpecConstant{Op|Composite} results, their result values must be the // same so are unifiable. case SpvOp::SpvOpSpecConstantOp: diff --git a/source/opt/vector_dce.h b/source/opt/vector_dce.h index 4d30b926..a55bda69 100644 --- a/source/opt/vector_dce.h +++ b/source/opt/vector_dce.h @@ -73,7 +73,7 @@ class VectorDCE : public MemPass { bool RewriteInstructions(Function* function, const LiveComponentMap& live_components); - // Makrs all DebugValue instructions that use |composite| for their values as + // Makes all DebugValue instructions that use |composite| for their values as // dead instructions by putting them into |dead_dbg_value|. void MarkDebugValueUsesAsDead(Instruction* composite, std::vector* dead_dbg_value); diff --git a/source/reduce/remove_struct_member_reduction_opportunity.cpp b/source/reduce/remove_struct_member_reduction_opportunity.cpp index da096e1e..e72ed351 100644 --- a/source/reduce/remove_struct_member_reduction_opportunity.cpp +++ b/source/reduce/remove_struct_member_reduction_opportunity.cpp @@ -153,7 +153,7 @@ void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices( next_type = type_inst->GetSingleWordInOperand(0); break; case SpvOpTypeStruct: { - // Struct types are special becuase (a) we may need to adjust the index + // Struct types are special because (a) we may need to adjust the index // being used, if the struct type is the one from which we are removing // a member, and (b) the type encountered by following the current index // is dependent on the value of the index. diff --git a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp index e72be625..cd0c4e4d 100644 --- a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp +++ b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp @@ -136,9 +136,9 @@ RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities( } } - // We now know those struct indices that are unsed, and we make a reduction + // We now know those struct indices that are unused, and we make a reduction // opportunity for each of them. By mapping each relevant member index to the - // structs in which it is unsed, we will group all opportunities to remove + // structs in which it is unused, we will group all opportunities to remove // member k of a struct (for some k) together. This reduces the likelihood // that opportunities to remove members from the same struct will be adjacent, // which is good because such opportunities mutually disable one another. diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp index dc20f689..29fbe551 100644 --- a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp +++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp @@ -96,7 +96,7 @@ StructuredConstructToBlockReductionOpportunityFinder::GetAvailableOpportunities( // This also means that we don't add a region. continue; } - // We have a reachable header block with a rechable merge that + // We have a reachable header block with a reachable merge that // postdominates the header: this means we have a new region. regions.emplace(&block, std::unordered_set()); } @@ -128,7 +128,7 @@ bool StructuredConstructToBlockReductionOpportunityFinder:: if (!block->WhileEachInst( [context, &header, ®ion](opt::Instruction* inst) -> bool { if (inst->result_id() == 0) { - // The instruction does not genreate a result id, thus it cannot + // The instruction does not generate a result id, thus it cannot // be referred to outside the region - this is fine. return true; } diff --git a/source/spirv_definition.h b/source/spirv_definition.h index 63a4ef0d..5dbd6ab2 100644 --- a/source/spirv_definition.h +++ b/source/spirv_definition.h @@ -27,7 +27,7 @@ typedef struct spv_header_t { uint32_t generator; uint32_t bound; uint32_t schema; // NOTE: Reserved - const uint32_t* instructions; // NOTE: Unfixed pointer to instruciton stream + const uint32_t* instructions; // NOTE: Unfixed pointer to instruction stream } spv_header_t; #endif // SOURCE_SPIRV_DEFINITION_H_ diff --git a/source/spirv_endian.h b/source/spirv_endian.h index c2540bec..b4927f31 100644 --- a/source/spirv_endian.h +++ b/source/spirv_endian.h @@ -31,7 +31,7 @@ uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high, spv_result_t spvBinaryEndianness(const spv_const_binary binary, spv_endianness_t* endian); -// Returns true if the given endianness matches the host's native endiannes. +// Returns true if the given endianness matches the host's native endianness. bool spvIsHostEndian(spv_endianness_t endian); #endif // SOURCE_SPIRV_ENDIAN_H_ diff --git a/source/spirv_target_env.h b/source/spirv_target_env.h index cc06deca..f3b0c2f6 100644 --- a/source/spirv_target_env.h +++ b/source/spirv_target_env.h @@ -40,7 +40,7 @@ std::string spvLogStringForEnv(spv_target_env env); // Returns a formatted list of all SPIR-V target environment names that // can be parsed by spvParseTargetEnv. -// |pad| is the number of space characters that the begining of each line +// |pad| is the number of space characters that the beginning of each line // except the first one will be padded with. // |wrap| is the max length of lines the user desires. Word-wrapping will // occur to satisfy this limit. diff --git a/source/util/bit_vector.h b/source/util/bit_vector.h index 3e189cb1..826d62f0 100644 --- a/source/util/bit_vector.h +++ b/source/util/bit_vector.h @@ -32,7 +32,7 @@ class BitVector { enum { kInitialNumBits = 1024 }; public: - // Creates a bit vector contianing 0s. + // Creates a bit vector containing 0s. BitVector(uint32_t reserved_size = kInitialNumBits) : bits_((reserved_size - 1) / kBitContainerSize + 1, 0) {} diff --git a/source/util/ilist.h b/source/util/ilist.h index 9837b09b..b7ecf01e 100644 --- a/source/util/ilist.h +++ b/source/util/ilist.h @@ -59,7 +59,7 @@ class IntrusiveList { // Moves the contents of the given list to the list being constructed. IntrusiveList(IntrusiveList&&); - // Destorys the list. Note that the elements of the list will not be deleted, + // Destroys the list. Note that the elements of the list will not be deleted, // but they will be removed from the list. virtual ~IntrusiveList(); diff --git a/source/util/parse_number.h b/source/util/parse_number.h index 729aac54..d0f2a09a 100644 --- a/source/util/parse_number.h +++ b/source/util/parse_number.h @@ -220,7 +220,7 @@ EncodeNumberStatus ParseAndEncodeIntegerNumber( std::function emit, std::string* error_msg); // Parses a floating point value of a given |type| from the given |text| and -// encodes the number by the given |emit| funciton. On success, returns +// encodes the number by the given |emit| function. On success, returns // EncodeNumberStatus::kSuccess and the parsed number will be consumed by the // given |emit| function word by word (least significant word first). On // failure, this function returns the error code of the encoding status and diff --git a/source/util/small_vector.h b/source/util/small_vector.h index 8f56268a..f1762a9f 100644 --- a/source/util/small_vector.h +++ b/source/util/small_vector.h @@ -366,7 +366,7 @@ class SmallVector { } } - // Upate the size. + // Update the size. size_ += num_of_new_elements; return pos; } @@ -452,7 +452,7 @@ class SmallVector { T* small_data_; // The actual data used to store the array elements. It must never be used - // directly, but must only be accesed through |small_data_|. + // directly, but must only be accessed through |small_data_|. typename std::aligned_storage::value>::type buffer[small_size]; diff --git a/source/util/timer.h b/source/util/timer.h index fc4b747b..08083119 100644 --- a/source/util/timer.h +++ b/source/util/timer.h @@ -206,16 +206,16 @@ class Timer { // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when // Timer::Stop() is called. It is used as the last status of CPU time. The - // resouce usage is measured by subtracting |cpu_before_| from it. + // resource usage is measured by subtracting |cpu_before_| from it. timespec cpu_after_; // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when // Timer::Stop() is called. It is used as the last status of WALL time. The - // resouce usage is measured by subtracting |wall_before_| from it. + // resource usage is measured by subtracting |wall_before_| from it. timespec wall_after_; // Variable to save the result of getrusage() when Timer::Stop() is called. It - // is used as the last status of USR time, SYS time, and RSS. Those resouce + // is used as the last status of USR time, SYS time, and RSS. Those resource // usages are measured by subtracting |usage_before_| from it. rusage usage_after_; diff --git a/source/val/basic_block.h b/source/val/basic_block.h index 5af4b9e4..47cd06d0 100644 --- a/source/val/basic_block.h +++ b/source/val/basic_block.h @@ -84,26 +84,26 @@ class BasicBlock { type_.set(type); } - /// Sets the immedate dominator of this basic block + /// Sets the immediate dominator of this basic block /// /// @param[in] dom_block The dominator block void SetImmediateDominator(BasicBlock* dom_block); - /// Sets the immedate post dominator of this basic block + /// Sets the immediate post dominator of this basic block /// /// @param[in] pdom_block The post dominator block void SetImmediatePostDominator(BasicBlock* pdom_block); - /// Returns the immedate dominator of this basic block + /// Returns the immediate dominator of this basic block BasicBlock* immediate_dominator(); - /// Returns the immedate dominator of this basic block + /// Returns the immediate dominator of this basic block const BasicBlock* immediate_dominator() const; - /// Returns the immedate post dominator of this basic block + /// Returns the immediate post dominator of this basic block BasicBlock* immediate_post_dominator(); - /// Returns the immedate post dominator of this basic block + /// Returns the immediate post dominator of this basic block const BasicBlock* immediate_post_dominator() const; /// Returns the label instruction for the block, or nullptr if not set. diff --git a/source/val/function.cpp b/source/val/function.cpp index 9ad68e86..f3292b0e 100644 --- a/source/val/function.cpp +++ b/source/val/function.cpp @@ -57,7 +57,7 @@ spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id, uint32_t type_id) { assert(current_block_ == nullptr && "RegisterFunctionParameter can only be called when parsing the binary " - "ouside of a block"); + "outside of a block"); // TODO(umar): Validate function parameter type order and count // TODO(umar): Use these variables to validate parameter type (void)parameter_id; @@ -130,7 +130,7 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) { undefined_blocks_.erase(block_id); current_block_ = &inserted_block->second; ordered_blocks_.push_back(current_block_); - } else if (success) { // Block doesn't exsist but this is not a definition + } else if (success) { // Block doesn't exist but this is not a definition undefined_blocks_.insert(block_id); } diff --git a/source/val/function.h b/source/val/function.h index 400bb634..2fe30bdc 100644 --- a/source/val/function.h +++ b/source/val/function.h @@ -73,8 +73,8 @@ class Function { /// Registers a variable in the current block /// - /// @param[in] type_id The type ID of the varaible - /// @param[in] id The ID of the varaible + /// @param[in] type_id The type ID of the variable + /// @param[in] id The ID of the variable /// @param[in] storage The storage of the variable /// @param[in] init_id The initializer ID of the variable /// @@ -197,10 +197,10 @@ class Function { /// been identified and dominators have been computed. int GetBlockDepth(BasicBlock* bb); - /// Prints a GraphViz digraph of the CFG of the current funciton + /// Prints a GraphViz digraph of the CFG of the current function void PrintDotGraph() const; - /// Prints a directed graph of the CFG of the current funciton + /// Prints a directed graph of the CFG of the current function void PrintBlocks() const; /// Registers execution model limitation such as "Feature X is only available @@ -285,7 +285,7 @@ class Function { /// The type of the return value uint32_t result_type_id_; - /// The control fo the funciton + /// The control fo the function SpvFunctionControlMask function_control_; /// The type of declaration of each function diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 64df67ca..ecc9fdb6 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -348,7 +348,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( } // Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi - // must only be preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. + // must only be preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine. if (auto error = ValidateAdjacency(*vstate)) return error; if (auto error = ValidateEntryPoints(*vstate)) return error; diff --git a/source/val/validate.h b/source/val/validate.h index 3fc183de..cb1d05a5 100644 --- a/source/val/validate.h +++ b/source/val/validate.h @@ -70,7 +70,7 @@ spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _); /// /// This function will iterate over all instructions and check for any required /// predecessor and/or successor instructions. e.g. SpvOpPhi must only be -/// preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. +/// preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine. /// /// @param[in] _ the validation state of the module /// diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp index 433330d7..bae9b5dc 100644 --- a/source/val/validate_arithmetics.cpp +++ b/source/val/validate_arithmetics.cpp @@ -155,7 +155,7 @@ spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { first_vector_num_components = num_components; } else if (num_components != first_vector_num_components) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to have the same number of componenets: " + << "Expected operands to have the same number of components: " << spvOpcodeString(opcode); } } diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index 26b2e94a..88abd754 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -675,7 +675,7 @@ spv_result_t ValidateStructuredSelections( } else if (terminator->opcode() == SpvOpSwitch) { if (!merge) { return _.diag(SPV_ERROR_INVALID_CFG, terminator) - << "OpSwitch must be preceeded by an OpSelectionMerge " + << "OpSwitch must be preceded by an OpSelectionMerge " "instruction"; } // Mark the targets as seen. @@ -917,7 +917,7 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { } } } - // If we have structed control flow, check that no block has a control + // If we have structured control flow, check that no block has a control // flow nesting depth larger than the limit. if (_.HasCapability(SpvCapabilityShader)) { const int control_flow_nesting_depth_limit = diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index eb610902..eb6caf0b 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -465,7 +465,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, return lhs.offset < rhs.offset; }); - // Now scan from lowest offest to highest offset. + // Now scan from lowest offset to highest offset. uint32_t nextValidOffset = 0; for (size_t ordered_member_idx = 0; ordered_member_idx < member_offsets.size(); ordered_member_idx++) { @@ -720,7 +720,7 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { return SPV_SUCCESS; } -// Checks whether proper decorations have been appied to the entry points. +// Checks whether proper decorations have been applied to the entry points. spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { for (uint32_t entry_point : vstate.entry_points()) { const auto& descs = vstate.entry_point_descriptions(entry_point); diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index b9f8e3c5..01cbcd25 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -129,7 +129,7 @@ spv_result_t ValidateUint32ConstantOperandForDebugInfo( } // True if the operand of a debug info instruction |inst| at |word_index| -// satisifies |expectation| that is given as a function. Otherwise, +// satisfies |expectation| that is given as a function. Otherwise, // returns false. bool DoesDebugInfoOperandMatchExpectation( const ValidationState_t& _, diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 037fab69..1209588c 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -502,7 +502,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, if (!_.IsIntVectorType(component_type) || _.GetDimension(component_type) != 2) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand ConstOffsets array componenets to be " + << "Expected Image Operand ConstOffsets array components to be " "int vectors of size 2"; } @@ -1043,7 +1043,7 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, << "Result from OpSampledImage instruction must not appear " "as operand for Op" << spvOpcodeString(static_cast(consumer_opcode)) - << ", since it is not specificed as taking an " + << ", since it is not specified as taking an " << "OpTypeSampledImage." << " Found result '" << _.getIdName(inst->id()) << "' as an operand of '" @@ -1672,7 +1672,7 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { << " components, but given only " << actual_coord_size; } - // TODO(atgoo@github.com) The spec doesn't explicitely say what the type + // TODO(atgoo@github.com) The spec doesn't explicitly say what the type // of texel should be. const uint32_t texel_type = _.GetOperandTypeId(inst, 2); if (!_.IsIntScalarOrVectorType(texel_type) && diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp index dad98673..3edf1637 100644 --- a/source/val/validate_instruction.cpp +++ b/source/val/validate_instruction.cpp @@ -297,7 +297,7 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { } // OpTerminateInvocation is special because it is enabled by Shader - // capability, but also requries a extension and/or version check. + // capability, but also requires an extension and/or version check. const bool capability_check_is_sufficient = inst->opcode() != SpvOpTerminateInvocation; @@ -406,7 +406,7 @@ spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) { // The instruction syntax is as follows: // OpSwitch literal label literal label ... // literal,label pairs come after the first 2 operands. - // It is guaranteed at this point that num_operands is an even numner. + // It is guaranteed at this point that num_operands is an even number. size_t num_pairs = (inst->operands().size() - 2) / 2; const unsigned int num_pairs_limit = _.options()->universal_limits_.max_switch_branches; diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index 29ba5831..1c5f70a3 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp @@ -225,7 +225,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, << _.VkErrorID(4638) << spvOpcodeString(opcode) << ": in Vulkan environment, Memory Scope cannot be CrossDevice"; } - // Vulkan 1.0 specifc rules + // Vulkan 1.0 specific rules if (_.context()->target_env == SPV_ENV_VULKAN_1_0 && value != SpvScopeDevice && value != SpvScopeWorkgroup && value != SpvScopeInvocation) { @@ -234,7 +234,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, << ": in Vulkan 1.0 environment Memory Scope is limited to " << "Device, Workgroup and Invocation"; } - // Vulkan 1.1 specifc rules + // Vulkan 1.1 specific rules if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 || _.context()->target_env == SPV_ENV_VULKAN_1_2) && value != SpvScopeDevice && value != SpvScopeWorkgroup && diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 8c02af1a..6f97321f 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -498,7 +498,7 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() { "inside of another function"); assert(in_block() == false && "RegisterFunctionParameter can only be called when parsing the binary " - "ouside of a block"); + "outside of a block"); current_function().RegisterFunctionEnd(); in_function_ = false; return SPV_SUCCESS; @@ -610,7 +610,7 @@ void ValidationState_t::RegisterStorageClassConsumer( if (message) { *message = errorVUID + - "in Vulkan evironment, Output Storage Class must not be " + "in Vulkan environment, Output Storage Class must not be " "used in GLCompute, RayGenerationKHR, IntersectionKHR, " "AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR " "execution models"; @@ -632,7 +632,7 @@ void ValidationState_t::RegisterStorageClassConsumer( if (message) { *message = errorVUID + - "in Vulkan evironment, Workgroup Storage Class is limited " + "in Vulkan environment, Workgroup Storage Class is limited " "to MeshNV, TaskNV, and GLCompute execution model"; } return false; @@ -1407,7 +1407,7 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return ""; } - // This large switch case is only searched when an error has occured. + // This large switch case is only searched when an error has occurred. // If an id is changed, the old case must be modified or removed. Each string // here is interpreted as being "implemented" diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 2ddfa4a9..89834a0d 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -67,7 +67,7 @@ class ValidationState_t { bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width? bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width? bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration - // and its vaules to be used without + // and its values to be used without // requiring any capability // Allow functionalities enabled by VariablePointers capability. @@ -797,7 +797,7 @@ class ValidationState_t { /// IDs that are entry points, ie, arguments to OpEntryPoint. std::vector entry_points_; - /// Maps an entry point id to its desciptions. + /// Maps an entry point id to its descriptions. std::unordered_map> entry_point_descriptions_; @@ -844,7 +844,7 @@ class ValidationState_t { // have the same pointer size (for physical pointer types). uint32_t pointer_size_and_alignment_; - /// NOTE: See correspoding getter functions + /// NOTE: See corresponding getter functions bool in_function_; /// The state of optional features. These are determined by capabilities diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp index ece750c5..f0810a35 100644 --- a/test/binary_parse_test.cpp +++ b/test/binary_parse_test.cpp @@ -604,7 +604,7 @@ INSTANTIATE_TEST_SUITE_P( MakeInstruction(SpvOpNop, {42})}), "Invalid instruction OpNop starting at word 5: expected " "no more operands after 1 words, but stated word count is 2."}, - // Supply several more unexpectd words. + // Supply several more unexpected words. {Concatenate({ExpectedHeaderForBound(1), MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}), "Invalid instruction OpNop starting at word 5: expected " diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp index 841bb2c6..1562057f 100644 --- a/test/c_interface_test.cpp +++ b/test/c_interface_test.cpp @@ -117,7 +117,7 @@ TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) { const spv_position_t& position, const char* message) { ++invocation; EXPECT_EQ(SPV_MSG_ERROR, level); - // The error happens at scanning the begining of second line. + // The error happens at scanning the beginning of second line. EXPECT_STREQ("input", source); EXPECT_EQ(1u, position.line); EXPECT_EQ(0u, position.column); diff --git a/test/ext_inst.cldebug100_test.cpp b/test/ext_inst.cldebug100_test.cpp index 4f1e1067..0bbdd3a9 100644 --- a/test/ext_inst.cldebug100_test.cpp +++ b/test/ext_inst.cldebug100_test.cpp @@ -581,7 +581,7 @@ INSTANTIATE_TEST_SUITE_P( OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest, ::testing::ValuesIn(std::vector({ // TODO(dneto): Should this be a list of sourc texts, - // to accomodate length limits? + // to accommodate length limits? CASE_I(Source), CASE_II(Source), }))); diff --git a/test/ext_inst.opencl_test.cpp b/test/ext_inst.opencl_test.cpp index 7547d922..d80a9bd7 100644 --- a/test/ext_inst.opencl_test.cpp +++ b/test/ext_inst.opencl_test.cpp @@ -233,7 +233,7 @@ INSTANTIATE_TEST_SUITE_P( CASE3(UMad_hi, u_mad_hi), // enum value 204 }))); -// OpenCL.std: 2.3 Common instrucitons +// OpenCL.std: 2.3 Common instructions INSTANTIATE_TEST_SUITE_P( OpenCLCommon, ExtInstOpenCLStdRoundTripTest, ::testing::ValuesIn(std::vector({ diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp index 0ad3e742..1286d38d 100644 --- a/test/fuzz/fuzzerutil_test.cpp +++ b/test/fuzz/fuzzerutil_test.cpp @@ -70,7 +70,7 @@ TEST(FuzzerUtilMaybeFindBlockTest, BasicTest) { ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id2) != nullptr); // Block with id 13 cannot be found. ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id3) != nullptr); - // Block with id 8 exisits but don't not of type OpLabel. + // Block with id 8 exists but don't not of type OpLabel. ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id4) != nullptr); } @@ -965,7 +965,7 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) { ASSERT_EQ( 91, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class)); - // A pointer with id=91 and pointee type 90 exisits, but the type should be + // A pointer with id=91 and pointee type 90 exists, but the type should be // input. ASSERT_EQ(0, fuzzerutil::MaybeGetPointerType(ir_context, 90, function_storage_class)); diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp index 2ae60c9d..7f069b55 100644 --- a/test/fuzz/transformation_add_parameter_test.cpp +++ b/test/fuzz/transformation_add_parameter_test.cpp @@ -367,7 +367,7 @@ TEST(TransformationAddParameterTest, NonPointerNotApplicableTest) { transformation_bad_3.IsApplicable(context.get(), transformation_context)); // Function with id 14 does not have any callers. - // Bad: Id 18 is not a vaild type. + // Bad: Id 18 is not a valid type. TransformationAddParameter transformation_bad_4(14, 50, 18, {{}}, 51); ASSERT_FALSE( transformation_bad_4.IsApplicable(context.get(), transformation_context)); diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp index ed8e00a6..4cbfed0f 100644 --- a/test/fuzz/transformation_add_type_int_test.cpp +++ b/test/fuzz/transformation_add_type_int_test.cpp @@ -87,7 +87,7 @@ TEST(TransformationAddTypeIntTest, IsApplicable) { transformation.IsApplicable(context.get(), transformation_context)); // By default SPIR-V does not support 16-bit integers. - // Below we add such capability, so the test should now be succesful. + // Below we add such capability, so the test should now be successful. context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt16); ASSERT_TRUE(TransformationAddTypeInt(7, 16, true) .IsApplicable(context.get(), transformation_context)); diff --git a/test/fuzz/transformation_adjust_branch_weights_test.cpp b/test/fuzz/transformation_adjust_branch_weights_test.cpp index 1bf2c593..5984a3e9 100644 --- a/test/fuzz/transformation_adjust_branch_weights_test.cpp +++ b/test/fuzz/transformation_adjust_branch_weights_test.cpp @@ -106,7 +106,7 @@ TEST(TransformationAdjustBranchWeightsTest, IsApplicableTest) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); - // Tests OpBranchConditional instruction with weigths. + // Tests OpBranchConditional instruction with weights. auto instruction_descriptor = MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); auto transformation = diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp index ffdb8bda..7edfd43d 100644 --- a/test/hex_float_test.cpp +++ b/test/hex_float_test.cpp @@ -1343,7 +1343,7 @@ struct StreamParseCase { template std::ostream& operator<<(std::ostream& os, const StreamParseCase& fspc) { os << "StreamParseCase(" << fspc.literal - << ", expect_succes:" << int(fspc.expect_success) << "," + << ", expect_success:" << int(fspc.expect_success) << "," << fspc.expected_suffix << "," << fspc.expected_value << ")"; return os; } diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index bc7dd712..25f85416 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -4323,7 +4323,7 @@ INSTANTIATE_TEST_SUITE_P( }, // Uint vector type spec constants. One vector has all component dead, - // another vector has one dead unsigend integer and one used unsigned + // another vector has one dead unsigned integer and one used unsigned // integer. { /* .used_consts = */ diff --git a/test/opt/assembly_builder.h b/test/opt/assembly_builder.h index 1673c092..b94e90f5 100644 --- a/test/opt/assembly_builder.h +++ b/test/opt/assembly_builder.h @@ -70,7 +70,7 @@ class AssemblyBuilder { static const uint32_t SPEC_ID_BASE = 200; public: - // Initalize a minimal SPIR-V assembly code as the template. The minimal + // Initialize a minimal SPIR-V assembly code as the template. The minimal // module contains an empty main function and some predefined names for the // main function. AssemblyBuilder() @@ -102,7 +102,7 @@ class AssemblyBuilder { }); } - // Appends OpName instructions to this builder. Instrcution strings that do + // Appends OpName instructions to this builder. Instruction strings that do // not start with 'OpName ' will be skipped. Returns the references of this // assembly builder. AssemblyBuilder& AppendNames(const std::vector& vec_asm_code) { diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp index 6903c4e7..9698fed2 100644 --- a/test/opt/block_merge_test.cpp +++ b/test/opt/block_merge_test.cpp @@ -884,7 +884,7 @@ TEST_F(BlockMergeTest, MergeHeaders) { ; CHECK-NEXT: [[header]] = OpLabel ; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]] ; CHECK: [[merge]] = OpLabel -; CHEKC: OpReturn +; CHECK: OpReturn OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" diff --git a/test/opt/code_sink_test.cpp b/test/opt/code_sink_test.cpp index f1bd1275..bf5029b6 100644 --- a/test/opt/code_sink_test.cpp +++ b/test/opt/code_sink_test.cpp @@ -378,7 +378,7 @@ TEST_F(CodeSinkTest, MoveReadOnlyLoadWithSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeArray %uint %uint_4 %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 @@ -419,7 +419,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeStruct %uint %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 @@ -460,7 +460,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfAtomicWithSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeStruct %uint %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp index 9c1ef2a3..b04c8f5e 100644 --- a/test/opt/dead_branch_elim_test.cpp +++ b/test/opt/dead_branch_elim_test.cpp @@ -1613,7 +1613,7 @@ OpBranch %7 %11 = OpLogicalOr %bool %true %false OpBranch %7 %7 = OpLabel -; This phi is in a block preceeding the merge %14! +; This phi is in a block preceding the merge %14! %8 = OpPhi %bool %10 %5 %11 %6 OpBranch %14 %14 = OpLabel diff --git a/test/opt/decoration_manager_test.cpp b/test/opt/decoration_manager_test.cpp index fcfbff06..c9fabe78 100644 --- a/test/opt/decoration_manager_test.cpp +++ b/test/opt/decoration_manager_test.cpp @@ -118,7 +118,7 @@ class DecorationManagerTest : public ::testing::Test { TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodesDecorateDecorateId) { IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); - // This parameter can be interprated both as { SpvDecorationConstant } + // This parameter can be interpreted both as { SpvDecorationConstant } // and also as a list of IDs: { 22 } const std::vector param{SpvDecorationConstant}; // OpDecorate %1 Constant @@ -137,7 +137,7 @@ TEST_F(DecorationManagerTest, TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodesDecorateDecorateString) { IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); - // This parameter can be interprated both as { SpvDecorationConstant } + // This parameter can be interpreted both as { SpvDecorationConstant } // and also as a null-terminated string with a single character with value 22. const std::vector param{SpvDecorationConstant}; // OpDecorate %1 Constant diff --git a/test/opt/def_use_test.cpp b/test/opt/def_use_test.cpp index 431f3dc2..0210095d 100644 --- a/test/opt/def_use_test.cpp +++ b/test/opt/def_use_test.cpp @@ -1656,7 +1656,7 @@ INSTANTIATE_TEST_SUITE_P( "OpGroupDecorate %1 %2 %3", }, }, - // memeber decorate + // member decorate { // code "OpMemberDecorate %1 0 RelaxedPrecision " diff --git a/test/opt/dominator_tree/generated.cpp b/test/opt/dominator_tree/generated.cpp index 534f770b..4fccef05 100644 --- a/test/opt/dominator_tree/generated.cpp +++ b/test/opt/dominator_tree/generated.cpp @@ -57,7 +57,7 @@ void check_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn, } } -// Check that x does not dominates y and vise versa +// Check that x does not dominates y and vice versa void check_no_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn, uint32_t x, uint32_t y) { SCOPED_TRACE("Check no domination for Basic Block " + std::to_string(x) + diff --git a/test/opt/eliminate_dead_const_test.cpp b/test/opt/eliminate_dead_const_test.cpp index 59f06f9f..87aab545 100644 --- a/test/opt/eliminate_dead_const_test.cpp +++ b/test/opt/eliminate_dead_const_test.cpp @@ -547,7 +547,7 @@ INSTANTIATE_TEST_SUITE_P( }, // Uint vector type spec constants. One vector has all component dead, - // another vector has one dead unsigend integer and one used unsigned + // another vector has one dead unsigned integer and one used unsigned // integer. { /* .used_consts = */ diff --git a/test/opt/flatten_decoration_test.cpp b/test/opt/flatten_decoration_test.cpp index d8d88677..63207fd2 100644 --- a/test/opt/flatten_decoration_test.cpp +++ b/test/opt/flatten_decoration_test.cpp @@ -43,7 +43,7 @@ OpName %Camera "Camera" )"; } -// Retuns types +// Returns types std::string TypesAndFunctionsAssembly() { return R"(%void = OpTypeVoid diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 009631c0..df8f3b12 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -7092,7 +7092,7 @@ using MatchingInstructionWithNoResultFoldingTest = // Test folding instructions that do not have a result. The instruction // that will be folded is the last instruction before the return. If there -// are multiple returns, there is not guarentee which one is used. +// are multiple returns, there is not guarantee which one is used. TEST_P(MatchingInstructionWithNoResultFoldingTest, Case) { const auto& tc = GetParam(); diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp index 3c23347d..057b909d 100644 --- a/test/opt/graphics_robust_access_test.cpp +++ b/test/opt/graphics_robust_access_test.cpp @@ -1242,7 +1242,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) { ; CHECK-DAG: %int_9 = OpConstant %int 9 ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel - ; This access chain is manufatured only so we can compute the array length. + ; This access chain is manufactured only so we can compute the array length. ; Note that the %int_9 is already clamped ; CHECK: %[[ssbo_base:\w+]] = )" << ac << R"( %[[ssbo_p]] %var %int_9 diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index d22f027c..cefd8e54 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -4238,7 +4238,7 @@ TEST_F(InlineTest, CreateConstantForInlinedAt) { // This shader causes CreateDebugInlinedAt to generate a constant. // Using the Constant manager would attempt to build the invalidated // DefUse manager during inlining which could cause an assert because - // the function is in an inconsistant state. This test verifies that + // the function is in an inconsistent state. This test verifies that // CreateDebugInlinedAt detects that the DefUse manager is disabled // and creates a duplicate constant safely without the Constant manager. // diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp index 1a42329b..4c271dee 100644 --- a/test/opt/inst_bindless_check_test.cpp +++ b/test/opt/inst_bindless_check_test.cpp @@ -9984,8 +9984,8 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { // // Compute shader // Geometry shader -// Tesselation control shader -// Tesselation eval shader +// Tessellation control shader +// Tessellation eval shader // OpImage // SampledImage variable diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp index 8123ffbb..c5fd6799 100644 --- a/test/opt/inst_debug_printf_test.cpp +++ b/test/opt/inst_debug_printf_test.cpp @@ -206,8 +206,8 @@ OpFunctionEnd // // Compute shader // Geometry shader -// Tesselation control shader -// Tesselation eval shader +// Tessellation control shader +// Tessellation eval shader // Vertex shader } // namespace diff --git a/test/opt/loop_optimizations/loop_descriptions.cpp b/test/opt/loop_optimizations/loop_descriptions.cpp index 4d2f989a..b3f4f440 100644 --- a/test/opt/loop_optimizations/loop_descriptions.cpp +++ b/test/opt/loop_optimizations/loop_descriptions.cpp @@ -298,7 +298,7 @@ TEST_F(PassClassTest, NoLoop) { /* Generated from following GLSL with latch block artificially inserted to be -seperate from continue. +separate from continue. #version 430 void main(void) { float x[10]; diff --git a/test/opt/loop_optimizations/loop_fission.cpp b/test/opt/loop_optimizations/loop_fission.cpp index 55b9c263..bc3ec39b 100644 --- a/test/opt/loop_optimizations/loop_fission.cpp +++ b/test/opt/loop_optimizations/loop_fission.cpp @@ -692,7 +692,7 @@ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(source, expected, true); // By passing 1 as argument we are using the constructor which makes the -// critera to split the loop be if the registers in the loop exceede 1. By +// criteria to split the loop be if the registers in the loop exceede 1. By // using this constructor we are also enabling multiple passes (disabled by // default). SinglePassRunAndCheck(source, expected_multiple_passes, true, diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp index ac0dfde7..b72305c8 100644 --- a/test/opt/loop_optimizations/unroll_simple.cpp +++ b/test/opt/loop_optimizations/unroll_simple.cpp @@ -886,7 +886,7 @@ OpFunctionEnd LoopUnroller loop_unroller; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); // By unrolling by a factor that doesn't divide evenly into the number of loop - // iterations we perfom an additional transform when partially unrolling to + // iterations we perform an additional transform when partially unrolling to // account for the remainder. SinglePassRunAndCheck>(text, output, false); } @@ -3118,7 +3118,7 @@ OpFunctionEnd /* Generated from following GLSL with latch block artificially inserted to be -seperate from continue. +separate from continue. #version 430 void main(void) { float x[10]; diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp index a3c2eed7..17a13650 100644 --- a/test/opt/module_test.cpp +++ b/test/opt/module_test.cpp @@ -52,7 +52,7 @@ inline std::unique_ptr BuildModule(std::string text) { } TEST(ModuleTest, ComputeIdBound) { - // Emtpy module case. + // Empty module case. EXPECT_EQ(1u, BuildModule("")->module()->ComputeIdBound()); // Sensitive to result id EXPECT_EQ(2u, BuildModule("%void = OpTypeVoid")->module()->ComputeIdBound()); diff --git a/test/opt/pass_manager_test.cpp b/test/opt/pass_manager_test.cpp index 22d5e22e..4f36d5b2 100644 --- a/test/opt/pass_manager_test.cpp +++ b/test/opt/pass_manager_test.cpp @@ -30,7 +30,7 @@ namespace { using spvtest::GetIdBound; using ::testing::Eq; -// A null pass whose construtors accept arguments +// A null pass whose constructors accept arguments class NullPassWithArgs : public NullPass { public: NullPassWithArgs(uint32_t) {} diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp index f1dd50ee..10f805b6 100644 --- a/test/opt/set_spec_const_default_value_test.cpp +++ b/test/opt/set_spec_const_default_value_test.cpp @@ -618,7 +618,7 @@ INSTANTIATE_TEST_SUITE_P( {"", SpecIdToValueBitPatternMap{}, ""}, // 1. Empty with non-empty values to set. {"", SpecIdToValueBitPatternMap{{1, {100}}, {2, {200}}}, ""}, - // 2. Baisc bool type. + // 2. Basic bool type. { // code "OpDecorate %1 SpecId 100\n" diff --git a/test/opt/unify_const_test.cpp b/test/opt/unify_const_test.cpp index 6ed21734..0d7c30b0 100644 --- a/test/opt/unify_const_test.cpp +++ b/test/opt/unify_const_test.cpp @@ -263,7 +263,7 @@ TEST_F(UnifyFrontEndConstantSingleTest, UnifyWithDecorationOnTypes) { // decorated flat struct "%flat_d = OpTypeStruct %int %float", "%_pf_flat_d = OpTypePointer Function %flat_d", - // perserved contants. %flat_1 and %flat_d has same members, but + // preserved constants. %flat_1 and %flat_d has same members, but // their type are different in decorations, so they should not be // used to replace each other. "%int_1 = OpConstant %int 1", @@ -682,7 +682,7 @@ INSTANTIATE_TEST_SUITE_P( // zero-valued composite constant built from zero-valued constant // component. inner_zero should not be replace by null_inner. "%inner_zero = OpConstantComposite %inner_struct %bool_zero %float_zero", - // zero-valued composite contant built from zero-valued constants + // zero-valued composite constant built from zero-valued constants // and null constants. "%outer_zero = OpConstantComposite %outer_struct %inner_zero %int_null %double_null", // outer_struct type null constant, it should not be replaced by @@ -820,7 +820,7 @@ INSTANTIATE_TEST_SUITE_P( { "%spec_signed_add_duplicate = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2", }, - // use duplicated contants in main + // use duplicated constants in main { "%int_var = OpVariable %_pf_int Function", "OpStore %int_var %spec_signed_add_duplicate", diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp index 3e117b8f..abae6a22 100644 --- a/test/text_to_binary.control_flow_test.cpp +++ b/test/text_to_binary.control_flow_test.cpp @@ -379,7 +379,7 @@ INSTANTIATE_TEST_SUITE_P( "OpTypeQueue", "OpTypePipe ReadOnly", - // Skip OpTypeForwardPointer becasuse it doesn't even produce a result + // Skip OpTypeForwardPointer because it doesn't even produce a result // ID. // At least one thing that isn't a type at all diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 6d4e674e..e5f152e4 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -197,7 +197,7 @@ INSTANTIATE_TEST_SUITE_P( SpvBuiltInSubgroupLtMask})}, }))); -// The old builtin names (with KHR suffix) still work in the assmebler, and +// The old builtin names (with KHR suffix) still work in the assembler, and // map to the enums without the KHR. INSTANTIATE_TEST_SUITE_P( SPV_KHR_shader_ballot_vulkan_1_1_alias_check, ExtensionAssemblyTest, diff --git a/test/text_to_binary_test.cpp b/test/text_to_binary_test.cpp index 99d9ed68..0b348e87 100644 --- a/test/text_to_binary_test.cpp +++ b/test/text_to_binary_test.cpp @@ -65,7 +65,7 @@ INSTANTIATE_TEST_SUITE_P( {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 1, "NotNaN"}, {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 2, "NotInf"}, {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotNaN|NotInf"}, - // Mask experssions are symmetric. + // Mask expressions are symmetric. {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN"}, // Repeating a value has no effect. {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN|NotInf"}, diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp index 856ad02c..4c093e9f 100644 --- a/test/val/val_arithmetics_test.cpp +++ b/test/val/val_arithmetics_test.cpp @@ -656,7 +656,7 @@ TEST_F(ValidateArithmetics, DotDifferentVectorSize) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Expected operands to have the same number of componenets: Dot")); + "Expected operands to have the same number of components: Dot")); } TEST_F(ValidateArithmetics, VectorTimesScalarSuccess) { diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index d1a030a8..a0308d59 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp @@ -280,7 +280,7 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanWrongStorageClass) { AnyVUID("VUID-StandaloneSpirv-None-04645")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to " + HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " "MeshNV, TaskNV, and GLCompute execution model")); } @@ -708,7 +708,7 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 AnyVUID("VUID-StandaloneSpirv-None-04645")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to " + HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " "MeshNV, TaskNV, and GLCompute execution model")); } diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 634a842e..76477468 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -65,7 +65,7 @@ class Block { /// Creates a Block with a given label /// /// @param[in]: label the label id of the block - /// @param[in]: type the branch instruciton that ends the block + /// @param[in]: type the branch instruction that ends the block explicit Block(std::string label, SpvOp type = SpvOpBranch) : label_(label), body_(), type_(type), successors_() {} @@ -3523,7 +3523,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeSwitchBad2) { @@ -3550,7 +3550,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) { @@ -3622,7 +3622,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) { @@ -3654,7 +3654,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeLoopBreakGood) { diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index ac057493..69257a58 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -1015,7 +1015,7 @@ TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayBad) { "type.")); } // TODO: Object of this type can only be created with OpVariable using the -// Unifrom Storage Class +// Uniform Storage Class TEST_F(ValidateIdWithMessage, OpTypeStructGood) { std::string spirv = kGLSL450MemoryModel + R"( @@ -2859,7 +2859,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -// Same code as the last test excect for an extra decoration on one of the +// Same code as the last test except for an extra decoration on one of the // members. With the relaxed rules, the code is still valid. TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) { std::string spirv = kGLSL450MemoryModel + R"( diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index bf71c398..c807e14c 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -3134,7 +3134,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayNotVector) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand ConstOffsets array componenets " + HasSubstr("Expected Image Operand ConstOffsets array components " "to be int vectors of size 2")); } @@ -3149,7 +3149,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayVectorWrongSize) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand ConstOffsets array componenets " + HasSubstr("Expected Image Operand ConstOffsets array components " "to be int vectors of size 2")); } diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp index 8fb80a46..364d514e 100644 --- a/test/val/val_limits_test.cpp +++ b/test/val/val_limits_test.cpp @@ -750,7 +750,7 @@ TEST_F(ValidateLimits, CustomizedControlFlowDepthBad) { } // Valid. The purpose here is to test the CFG depth calculation code when a loop -// continue target is the loop iteself. It also exercises the case where a loop +// continue target is the loop itself. It also exercises the case where a loop // is unreachable. TEST_F(ValidateLimits, ControlFlowNoEntryToLoopGood) { std::string str = header + R"( diff --git a/test/val/val_literals_test.cpp b/test/val/val_literals_test.cpp index 6eadf321..7c9aad67 100644 --- a/test/val/val_literals_test.cpp +++ b/test/val/val_literals_test.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Validation tests for ilegal literals +// Validation tests for illegal literals #include #include diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp index 35f6a8d5..ae4047b9 100644 --- a/test/val/val_storage_test.cpp +++ b/test/val/val_storage_test.cpp @@ -280,7 +280,7 @@ TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) { AnyVUID("VUID-StandaloneSpirv-None-04644")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan evironment, Output Storage Class must not be used " + HasSubstr("in Vulkan environment, Output Storage Class must not be used " "in GLCompute, RayGenerationKHR, IntersectionKHR, AnyHitKHR, " "ClosestHitKHR, MissKHR, or CallableKHR execution models")); } diff --git a/tools/cfg/bin_to_dot.cpp b/tools/cfg/bin_to_dot.cpp index 2561eea4..72e7693a 100644 --- a/tools/cfg/bin_to_dot.cpp +++ b/tools/cfg/bin_to_dot.cpp @@ -57,13 +57,13 @@ class DotConverter { // Ends processing for the current block, emitting its dot code. void FlushBlock(const std::vector& successors); - // The ID of the current functio, or 0 if outside of a function. + // The ID of the current function, or 0 if outside of a function. uint32_t current_function_id_ = 0; // The ID of the current basic block, or 0 if outside of a block. uint32_t current_block_id_ = 0; - // Have we completed processing for the entry block to this fuction? + // Have we completed processing for the entry block to this function? bool seen_function_entry_block_ = false; // The Id of the merge block for this block if it exists, or 0 otherwise. diff --git a/tools/cfg/bin_to_dot.h b/tools/cfg/bin_to_dot.h index 4de2e07f..a61c9759 100644 --- a/tools/cfg/bin_to_dot.h +++ b/tools/cfg/bin_to_dot.h @@ -20,7 +20,7 @@ #include "spirv-tools/libspirv.h" // Dumps the control flow graph for the given module to the output stream. -// Returns SPV_SUCCESS on succes. +// Returns SPV_SUCCESS on success. spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words, size_t num_words, std::iostream* out, spv_diagnostic* diagnostic); diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 5610b248..63511a6a 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -44,7 +44,7 @@ struct OptStatus { // initialization and setup. Note that |source| and |position| are irrelevant // here because we are still not processing a SPIR-V input file. void opt_diagnostic(spv_message_level_t level, const char* /*source*/, - const spv_position_t& /*positon*/, const char* message) { + const spv_position_t& /*position*/, const char* message) { if (level == SPV_MSG_ERROR) { fprintf(stderr, "error: "); } diff --git a/tools/sva/README.md b/tools/sva/README.md index d80b4d2c..cd3d13c9 100644 --- a/tools/sva/README.md +++ b/tools/sva/README.md @@ -1,6 +1,6 @@ # SVA -SPIR-V Assember for WebGPU. The SPIR-V Assembler is a JavaScript library to +SPIR-V Assembler for WebGPU. The SPIR-V Assembler is a JavaScript library to convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has the following limitations. diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py index 9ccf410b..74aa2829 100755 --- a/utils/generate_grammar_tables.py +++ b/utils/generate_grammar_tables.py @@ -23,7 +23,7 @@ import re PYGEN_VARIABLE_PREFIX = 'pygen_variable' # Extensions to recognize, but which don't necessarily come from the SPIR-V -# core or KHR grammar files. Get this list from the SPIR-V registery web page. +# core or KHR grammar files. Get this list from the SPIR-V registry web page. # NOTE: Only put things on this list if it is not in those grammar files. EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ SPV_AMD_gcn_shader diff --git a/utils/git-sync-deps b/utils/git-sync-deps index eecfbe93..7a7e606f 100755 --- a/utils/git-sync-deps +++ b/utils/git-sync-deps @@ -168,7 +168,7 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose): with open(os.devnull, 'w') as devnull: # If this fails, we will fetch before trying again. Don't spam user - # with error infomation. + # with error information. if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable], cwd=directory, stderr=devnull): # if this succeeds, skip slow `git fetch`. diff --git a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go index b8436d27..44dd2205 100644 --- a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go +++ b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go @@ -48,7 +48,7 @@ const ( requestDone ) -// Request is sent to a server to represent a Call or Notify operaton. +// Request is sent to a server to represent a Call or Notify operation. type Request struct { conn *Conn cancel context.CancelFunc diff --git a/utils/vscode/src/lsp/jsonrpc2/wire.go b/utils/vscode/src/lsp/jsonrpc2/wire.go index 3e31c340..fed9a25b 100644 --- a/utils/vscode/src/lsp/jsonrpc2/wire.go +++ b/utils/vscode/src/lsp/jsonrpc2/wire.go @@ -44,7 +44,7 @@ const ( CodeServerOverloaded = -32000 ) -// WireRequest is sent to a server to represent a Call or Notify operaton. +// WireRequest is sent to a server to represent a Call or Notify operation. type WireRequest struct { // VersionTag is always encoded as the string "2.0" VersionTag VersionTag `json:"jsonrpc"` diff --git a/utils/vscode/src/lsp/protocol/tsprotocol.go b/utils/vscode/src/lsp/protocol/tsprotocol.go index 29dd9867..e0a35946 100644 --- a/utils/vscode/src/lsp/protocol/tsprotocol.go +++ b/utils/vscode/src/lsp/protocol/tsprotocol.go @@ -143,7 +143,7 @@ type WorkspaceFoldersServerCapabilities struct { * change notifications. * * If a strings is provided the string is treated as a ID - * under which the notification is registed on the client + * under which the notification is registered on the client * side. The ID can be used to unregister for these events * using the `client/unregisterCapability` request. */ @@ -162,7 +162,7 @@ type WorkspaceFolder struct { /*Name defined: * The name of the workspace folder. Used to refer to this - * workspace folder in thge user interface. + * workspace folder in the user interface. */ Name string `json:"name"` } @@ -1129,7 +1129,7 @@ type ServerCapabilities struct { * change notifications. * * If a strings is provided the string is treated as a ID - * under which the notification is registed on the client + * under which the notification is registered on the client * side. The ID can be used to unregister for these events * using the `client/unregisterCapability` request. */ @@ -1803,7 +1803,7 @@ type CompletionOptions struct { /*AllCommitCharacters defined: * The list of all possible characters that commit a completion. This field can be used - * if clients don't support individual commmit characters per completion item. See + * if clients don't support individual commit characters per completion item. See * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` * * @since 3.2.0 @@ -2881,7 +2881,7 @@ type Color struct { type ColorInformation struct { /*Range defined: - * The range in the document where this color appers. + * The range in the document where this color appears. */ Range Range `json:"range"` @@ -3652,7 +3652,7 @@ type CodeActionContext struct { /*Diagnostics defined: * An array of diagnostics known on the client side overlapping the range provided to the - * `textDocument/codeAction` request. They are provied so that the server knows which + * `textDocument/codeAction` request. They are provided so that the server knows which * errors are currently presented to the user for the given range. There is no guarantee * that these accurately reflect the error state of the resource. The primary parameter * to compute code actions is the provided range. @@ -4081,13 +4081,13 @@ const ( /*TextOnlyTransactional defined: * If the workspace edit contains only textual file changes they are executed transactional. * If resource changes (create, rename or delete file) are part of the change the failure - * handling startegy is abort. + * handling strategy is abort. */ TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" /*Undo defined: * The client tries to undo the operations already executed. But there is no - * guaruntee that this is succeeding. + * guarantee that this is succeeding. */ Undo FailureHandlingKind = "undo" diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go index cc6f3332..4c0fa8f7 100644 --- a/utils/vscode/src/parser/parser.go +++ b/utils/vscode/src/parser/parser.go @@ -798,7 +798,7 @@ type Identifier struct { References []*Token // all the places the identifier was referenced } -// Severity is an enumerator of diagnositc seeverities +// Severity is an enumerator of diagnostic severities type Severity int // Severity levels -- cgit v1.2.3 From a73e724359a274d7cf4f4248eba5be1e7764fbfd Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 26 Jan 2022 15:13:47 -0500 Subject: Fix typo in comment: SPIR-V section for debug instructions (#4683) Fixes: #4682 --- include/spirv-tools/optimizer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 0b29142e..fdb2e648 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -227,7 +227,7 @@ Optimizer::PassToken CreateNullPass(); // Creates a strip-debug-info pass. // A strip-debug-info pass removes all debug instructions (as documented in -// Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized. +// Section 3.42.2 of the SPIR-V spec) of the SPIR-V module to be optimized. Optimizer::PassToken CreateStripDebugInfoPass(); // [Deprecated] This will create a strip-nonsemantic-info pass. See below. -- cgit v1.2.3 From 20b122b2e0d43fcc322a383354d1a3f4514e3757 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Fri, 28 Jan 2022 21:12:03 -0500 Subject: Fix handling of Nontemporal image operand (#4692) * Nontemporal image operand shouldn't expect a following operand --- source/val/validate_image.cpp | 3 ++- test/val/val_image_test.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 1209588c..b12d1e82 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -260,7 +260,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask | SpvImageOperandsVolatileTexelKHRMask | SpvImageOperandsSignExtendMask | - SpvImageOperandsZeroExtendMask); + SpvImageOperandsZeroExtendMask | + SpvImageOperandsNontemporalMask); size_t expected_num_image_operand_words = spvtools::utils::CountSetBits(mask_bits_having_operands); if (mask & SpvImageOperandsGradMask) { diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index c807e14c..a11d07ce 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -6120,6 +6120,39 @@ OpMemoryModel Logical GLSL450 "not be Buffer")); } +TEST_F(ValidateImage, NonTemporalImage) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 " " %4 %5 +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui +%13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%4 = OpVariable %_ptr_Input_v4float Input +%v2float = OpTypeVector %float 2 +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%2 = OpFunction %void None %8 +%8224 = OpLabel +%6 = OpLoad %13 %5 +%19 = OpLoad %v4float %4 +%20 = OpVectorShuffle %v2float %19 %19 0 1 +%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19 +%65312 = OpImageSampleImplicitLod %v4float %6 %21 Nontemporal +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + } // namespace } // namespace val } // namespace spvtools -- cgit v1.2.3 From 58fa443749935aa5e97ae544c2e0574835814e0b Mon Sep 17 00:00:00 2001 From: Paul Thomson Date: Tue, 8 Feb 2022 15:23:04 +0000 Subject: Update Android.bp Bug: b/201652781 Change-Id: I64f46b43bd0b5bbb5ab3dca5aeb83804183b3c14 --- Android.bp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/Android.bp b/Android.bp index 8aeedaa7..ca6b9f59 100644 --- a/Android.bp +++ b/Android.bp @@ -39,7 +39,7 @@ genrule { "extension_enum.inc", "glsl.std.450.insts.inc", "nonsemantic.clspvreflection.insts.inc", - "nonsemantic.vulkan.debuginfo.100.insts.inc", + "nonsemantic.shader.debuginfo.100.insts.inc", "opencl.debuginfo.100.insts.inc", "opencl.std.insts.inc", "operand.kinds-unified1.inc", @@ -52,6 +52,7 @@ genrule { ":deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json", ":deqp_spirv_headers_unified1_extinst.glsl.std.450.grammar.json", ":deqp_spirv_headers_unified1_extinst.nonsemantic.clspvreflection.grammar.json", + ":deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json", ":deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json", ":deqp_spirv_headers_unified1_extinst.opencl.std.100.grammar.json", ":deqp_spirv_headers_unified1_extinst.spv-amd-gcn-shader.grammar.json", @@ -59,42 +60,40 @@ genrule { ":deqp_spirv_headers_unified1_extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json", ":deqp_spirv_headers_unified1_extinst.spv-amd-shader-trinary-minmax.grammar.json", ":deqp_spirv_headers_unified1_spirv.core.grammar.json", - "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json", ], tool_files: ["utils/generate_grammar_tables.py"], cmd: - "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json) --vendor-insts-output=$(location spv-amd-shader-explicit-vertex-parameter.insts.inc) --vendor-operand-kind-prefix=; "+ - "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-gcn-shader.grammar.json) --vendor-insts-output=$(location spv-amd-gcn-shader.insts.inc) --vendor-operand-kind-prefix=; "+ - "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-trinary-minmax.grammar.json) --vendor-insts-output=$(location spv-amd-shader-trinary-minmax.insts.inc) --vendor-operand-kind-prefix=; "+ + "$(location) --extinst-glsl-grammar=$(location :deqp_spirv_headers_unified1_extinst.glsl.std.450.grammar.json) --glsl-insts-output=$(location glsl.std.450.insts.inc); "+ + "$(location) --extinst-opencl-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.std.100.grammar.json) --opencl-insts-output=$(location opencl.std.insts.inc); "+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --vendor-insts-output=$(location debuginfo.insts.inc) --vendor-operand-kind-prefix=; "+ - "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-ballot.grammar.json) --vendor-insts-output=$(location spv-amd-shader-ballot.insts.inc) --vendor-operand-kind-prefix=; "+ - "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --vendor-insts-output=$(location opencl.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=CLDEBUG100_; "+ - "$(location) --extinst-vendor-grammar=$(location source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json) --vendor-insts-output=$(location nonsemantic.vulkan.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=VKDEBUG100_; "+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.nonsemantic.clspvreflection.grammar.json) --vendor-insts-output=$(location nonsemantic.clspvreflection.insts.inc) --vendor-operand-kind-prefix=; "+ - "$(location) --extinst-opencl-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.std.100.grammar.json) --opencl-insts-output=$(location opencl.std.insts.inc); "+ - "$(location) --extinst-glsl-grammar=$(location :deqp_spirv_headers_unified1_extinst.glsl.std.450.grammar.json) --glsl-insts-output=$(location glsl.std.450.insts.inc); "+ + "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json) --vendor-insts-output=$(location nonsemantic.shader.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=SHDEBUG100_; "+ + "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --vendor-insts-output=$(location opencl.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=CLDEBUG100_; "+ + "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-gcn-shader.grammar.json) --vendor-insts-output=$(location spv-amd-gcn-shader.insts.inc) --vendor-operand-kind-prefix=; "+ + "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-ballot.grammar.json) --vendor-insts-output=$(location spv-amd-shader-ballot.insts.inc) --vendor-operand-kind-prefix=; "+ + "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json) --vendor-insts-output=$(location spv-amd-shader-explicit-vertex-parameter.insts.inc) --vendor-operand-kind-prefix=; "+ + "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-trinary-minmax.grammar.json) --vendor-insts-output=$(location spv-amd-shader-trinary-minmax.insts.inc) --vendor-operand-kind-prefix=; "+ "$(location) --spirv-core-grammar=$(location :deqp_spirv_headers_unified1_spirv.core.grammar.json) --extinst-debuginfo-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --extinst-cldebuginfo100-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --core-insts-output=$(location core.insts-unified1.inc) --operand-kinds-output=$(location operand.kinds-unified1.inc); "+ "$(location) --spirv-core-grammar=$(location :deqp_spirv_headers_unified1_spirv.core.grammar.json) --extinst-debuginfo-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --extinst-cldebuginfo100-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --extension-enum-output=$(location extension_enum.inc) --enum-string-mapping-output=$(location enum_string_mapping.inc); " } - genrule { name: "deqp_spvtools_generate_language_headers", out: [ "DebugInfo.h", + "NonSemanticShaderDebugInfo100.h", "OpenCLDebugInfo100.h", - "NonSemanticVulkanDebugInfo100.h", ], srcs: [ ":deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json", + ":deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json", ":deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json", - "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json", ], tool_files: ["utils/generate_language_headers.py"], - cmd: + cmd: "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --extinst-output-path=$(location DebugInfo.h); "+ - "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --extinst-output-path=$(location OpenCLDebugInfo100.h); "+ - "$(location) --extinst-grammar=$(location source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json) --extinst-output-path=$(location NonSemanticVulkanDebugInfo100.h); " + "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json) --extinst-output-path=$(location NonSemanticShaderDebugInfo100.h); "+ + "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --extinst-output-path=$(location OpenCLDebugInfo100.h); " } genrule { -- cgit v1.2.3