diff --git a/3rdparty/spirv-tools/Android.mk b/3rdparty/spirv-tools/Android.mk index a6278af0d..d2fe6a66b 100644 --- a/3rdparty/spirv-tools/Android.mk +++ b/3rdparty/spirv-tools/Android.mk @@ -117,6 +117,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/inline_exhaustive_pass.cpp \ source/opt/inline_opaque_pass.cpp \ source/opt/inst_bindless_check_pass.cpp \ + source/opt/inst_buff_addr_check_pass.cpp \ source/opt/instruction.cpp \ source/opt/instruction_list.cpp \ source/opt/instrument_pass.cpp \ @@ -171,7 +172,8 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/upgrade_memory_model.cpp \ source/opt/value_number_table.cpp \ source/opt/vector_dce.cpp \ - source/opt/workaround1209.cpp + source/opt/workaround1209.cpp \ + source/opt/wrap_opkill.cpp # Locations of grammar files. # diff --git a/3rdparty/spirv-tools/BUILD.gn b/3rdparty/spirv-tools/BUILD.gn index 84b21e13b..6237af388 100644 --- a/3rdparty/spirv-tools/BUILD.gn +++ b/3rdparty/spirv-tools/BUILD.gn @@ -535,6 +535,8 @@ static_library("spvtools_opt") { "source/opt/inline_pass.h", "source/opt/inst_bindless_check_pass.cpp", "source/opt/inst_bindless_check_pass.h", + "source/opt/inst_buff_addr_check_pass.cpp", + "source/opt/inst_buff_addr_check_pass.h", "source/opt/instruction.cpp", "source/opt/instruction.h", "source/opt/instruction_list.cpp", @@ -650,6 +652,8 @@ static_library("spvtools_opt") { "source/opt/vector_dce.h", "source/opt/workaround1209.cpp", "source/opt/workaround1209.h", + "source/opt/wrap_opkill.cpp", + "source/opt/wrap_opkill.h", ] deps = [ diff --git a/3rdparty/spirv-tools/CHANGES b/3rdparty/spirv-tools/CHANGES index fd549fa35..57afc6387 100644 --- a/3rdparty/spirv-tools/CHANGES +++ b/3rdparty/spirv-tools/CHANGES @@ -18,7 +18,7 @@ v2019.4 2019-08-08 - Handle nested breaks from switches. (#2624) - Optimizer: Handle array type with OpSpecConstantOp length (#2652) - Perform merge return with single return in loop. (#2714) - - Add —preserve-bindings and —preserve-spec-constants (#2693) + - Add --preserve-bindings and --preserve-spec-constants (#2693) - Remove Common Uniform Elimination Pass (#2731) - Allow ray tracing shaders in inst bindle check pass. (#2733) - Add pass to inject code for robust-buffer-access semantics (#2771) diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index eac7f4217..287af1d6f 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-2-g95386f9e" +"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-16-g8336d192" diff --git a/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp b/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp index dfd6e358f..681d00887 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp @@ -23,8 +23,9 @@ // communicate with shaders instrumented by passes created by: // // CreateInstBindlessCheckPass +// CreateInstBuffAddrCheckPass // -// More detailed documentation of this routine can be found in optimizer.hpp +// More detailed documentation of these routines can be found in optimizer.hpp namespace spvtools { @@ -157,6 +158,12 @@ static const int kInst2BindlessUninitOutDescIndex = kInst2StageOutCnt + 1; static const int kInst2BindlessUninitOutUnused = kInst2StageOutCnt + 2; static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3; +// A buffer address unalloc error will output the 64-bit pointer in +// two 32-bit pieces, lower bits first. +static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1; +static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2; +static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3; + // DEPRECATED static const int kInstBindlessOutDescIndex = kInstStageOutCnt + 1; static const int kInstBindlessOutDescBound = kInstStageOutCnt + 2; @@ -171,6 +178,7 @@ static const int kInst2MaxOutCnt = kInst2StageOutCnt + 3; // These are the possible validation error codes. static const int kInstErrorBindlessBounds = 0; static const int kInstErrorBindlessUninit = 1; +static const int kInstErrorBuffAddrUnallocRef = 2; // Direct Input Buffer Offsets // @@ -187,14 +195,16 @@ static const int kDebugInputDataOffset = 0; // These are the bindings for the different buffers which are // read or written by the instrumentation passes. // -// This is the output buffer written by InstBindlessCheckPass -// and possibly other future validations. +// This is the output buffer written by InstBindlessCheckPass, +// InstBuffAddrCheckPass, and possibly other future validations. static const int kDebugOutputBindingStream = 0; -// The binding for the input buffer read by InstBindlessCheckPass and -// possibly other future validations. +// The binding for the input buffer read by InstBindlessCheckPass. static const int kDebugInputBindingBindless = 1; +// The binding for the input buffer read by InstBuffAddrCheckPass. +static const int kDebugInputBindingBuffAddr = 2; + // Bindless Validation Input Buffer Format // // An input buffer for bindless validation consists of a single array of @@ -216,6 +226,31 @@ static const int kDebugInputBindlessOffsetReserved = 0; // Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ] static const int kDebugInputBindlessOffsetLengths = 1; +// Buffer Device Address Input Buffer Format +// +// An input buffer for buffer device address validation consists of a single +// array of unsigned 64-bit integers we will call Data[]. This array is +// formatted as follows: +// +// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer +// addresses. The list is terminated with the address 0xffffffffffffffff. +// If 0x0 is not a valid buffer address, this address is inserted at the +// start of the list. +// +static const int kDebugInputBuffAddrPtrOffset = 1; +// +// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which +// gives an offset to the start of the buffer length data. More +// specifically, for a buffer whose pointer is located at input buffer offset +// i, the length is located at: +// +// Data[ i - kDebugInputBuffAddrPtrOffset +// + Data[ kDebugInputBuffAddrLengthOffset ] ] +// +// The length associated with the 0xffffffffffffffff address is zero. If +// not a valid buffer, the length associated with the 0x0 address is zero. +static const int kDebugInputBuffAddrLengthOffset = 0; + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_ diff --git a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp index d442b97f5..cec1f1683 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp @@ -729,6 +729,30 @@ Optimizer::PassToken CreateInstBindlessCheckPass( uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false, bool input_init_enable = false, uint32_t version = 1); +// Create a pass to instrument physical buffer address checking +// This pass instruments all physical buffer address references to check that +// all referenced bytes fall in a valid buffer. If the reference is +// invalid, a record is written to the debug output buffer (if space allows) +// and a null value is returned. This pass is designed to support buffer +// address validation in the Vulkan validation layers. +// +// Dead code elimination should be run after this pass as the original, +// potentially invalid code is not removed and could cause undefined behavior, +// including crashes. Instruction simplification would likely also be +// beneficial. It is also generally recommended that this pass (and all +// instrumentation passes) be run after any legalization and optimization +// passes. This will give better analysis for the instrumentation and avoid +// potentially de-optimizing the instrument code, for example, inlining +// the debug record output function throughout the module. +// +// The instrumentation will read and write buffers in debug +// descriptor set |desc_set|. It will write |shader_id| in each output record +// to identify the shader module which generated the record. +// |version| specifies the output buffer record format. +Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, + uint32_t shader_id, + uint32_t version = 2); + // Create a pass to upgrade to the VulkanKHR memory model. // This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR. // Additionally, it modifies memory, image, atomic and barrier operations to @@ -795,6 +819,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass(); // for the first index. Optimizer::PassToken CreateDescriptorScalarReplacementPass(); +// Create a pass to replace all OpKill instruction with a function call to a +// function that has a single OpKill. This allows more code to be inlined. +Optimizer::PassToken CreateWrapOpKillPass(); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/3rdparty/spirv-tools/source/opt/CMakeLists.txt b/3rdparty/spirv-tools/source/opt/CMakeLists.txt index 2ebad512a..bf7fe1370 100644 --- a/3rdparty/spirv-tools/source/opt/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/opt/CMakeLists.txt @@ -55,6 +55,7 @@ set(SPIRV_TOOLS_OPT_SOURCES inline_opaque_pass.h inline_pass.h inst_bindless_check_pass.h + inst_buff_addr_check_pass.h instruction.h instruction_list.h instrument_pass.h @@ -113,6 +114,7 @@ set(SPIRV_TOOLS_OPT_SOURCES value_number_table.h vector_dce.h workaround1209.h + wrap_opkill.h aggressive_dead_code_elim_pass.cpp basic_block.cpp @@ -157,6 +159,7 @@ set(SPIRV_TOOLS_OPT_SOURCES inline_opaque_pass.cpp inline_pass.cpp inst_bindless_check_pass.cpp + inst_buff_addr_check_pass.cpp instruction.cpp instruction_list.cpp instrument_pass.cpp @@ -212,6 +215,7 @@ set(SPIRV_TOOLS_OPT_SOURCES value_number_table.cpp vector_dce.cpp workaround1209.cpp + wrap_opkill.cpp ) if(MSVC) diff --git a/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp b/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp index 751786cfe..00757e351 100644 --- a/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp +++ b/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp @@ -527,6 +527,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, pointer_type->storage_class()); uint32_t new_pointer_type_id = context()->get_type_mgr()->GetTypeInstruction(&pointerTy); + if (new_pointer_type_id == 0) { + return false; + } if (new_pointer_type_id != use->type_id()) { return CanUpdateUses(use, new_pointer_type_id); @@ -542,6 +545,9 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, const analysis::Type* new_type = type_mgr->GetMemberType(type, access_chain); uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type); + if (new_type_id == 0) { + return false; + } if (new_type_id != use->type_id()) { return CanUpdateUses(use, new_type_id); diff --git a/3rdparty/spirv-tools/source/opt/desc_sroa.cpp b/3rdparty/spirv-tools/source/opt/desc_sroa.cpp index 36256ffaf..1f25b33b8 100644 --- a/3rdparty/spirv-tools/source/opt/desc_sroa.cpp +++ b/3rdparty/spirv-tools/source/opt/desc_sroa.cpp @@ -14,7 +14,7 @@ #include "source/opt/desc_sroa.h" -#include +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -248,6 +248,24 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable( context()->AddAnnotationInst(std::move(new_decoration)); } + // Create a new OpName for the replacement variable. + for (auto p : context()->GetNames(var->result_id())) { + Instruction* name_inst = p.second; + std::string name_str = utils::MakeString(name_inst->GetOperand(1).words); + name_str += "["; + name_str += utils::ToString(idx); + name_str += "]"; + + std::unique_ptr new_name(new Instruction( + context(), SpvOpName, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}})); + Instruction* new_name_inst = new_name.get(); + context()->AddDebug2Inst(std::move(new_name)); + get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst); + } + return id; } diff --git a/3rdparty/spirv-tools/source/opt/graphics_robust_access_pass.h b/3rdparty/spirv-tools/source/opt/graphics_robust_access_pass.h index d330c9dd1..215cbf12b 100644 --- a/3rdparty/spirv-tools/source/opt/graphics_robust_access_pass.h +++ b/3rdparty/spirv-tools/source/opt/graphics_robust_access_pass.h @@ -77,10 +77,6 @@ class GraphicsRobustAccessPass : public Pass { // type. Ignores any value bits beyond the width of the type. Instruction* GetValueForType(uint64_t value, const analysis::Integer* type); - // Returns the unsigned value for the given constant. Assumes it's at most - // 64 bits wide. - uint64_t GetUnsignedValueForConstant(const analysis::Constant* c); - // Converts an integer value to an unsigned wider integer type, using either // sign extension or zero extension. The new instruction is inserted // immediately before |before_inst|, and is analyzed for definitions and uses. diff --git a/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp b/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp new file mode 100644 index 000000000..662937924 --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp @@ -0,0 +1,427 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "inst_buff_addr_check_pass.h" + +namespace spvtools { +namespace opt { + +uint32_t InstBuffAddrCheckPass::CloneOriginalReference( + Instruction* ref_inst, InstructionBuilder* builder) { + // Clone original ref with new result id (if load) + assert( + (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) && + "unexpected ref"); + std::unique_ptr new_ref_inst(ref_inst->Clone(context())); + uint32_t ref_result_id = ref_inst->result_id(); + uint32_t new_ref_id = 0; + if (ref_result_id != 0) { + new_ref_id = TakeNextId(); + new_ref_inst->SetResultId(new_ref_id); + } + // Register new reference and add to new block + Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst)); + uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()]; + if (new_ref_id != 0) + get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id); + return new_ref_id; +} + +bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) { + if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore) + return false; + uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0); + analysis::DefUseManager* du_mgr = get_def_use_mgr(); + Instruction* ptr_inst = du_mgr->GetDef(ptr_id); + if (ptr_inst->opcode() != SpvOpAccessChain) return false; + uint32_t ptr_ty_id = ptr_inst->type_id(); + Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id); + if (ptr_ty_inst->GetSingleWordInOperand(0) != + SpvStorageClassPhysicalStorageBufferEXT) + return false; + return true; +} + +// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ?? +void InstBuffAddrCheckPass::GenCheckCode( + uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id, + uint32_t stage_idx, Instruction* ref_inst, + std::vector>* new_blocks) { + BasicBlock* back_blk_ptr = &*new_blocks->back(); + InstructionBuilder builder( + context(), back_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen conditional branch on check_id. Valid branch generates original + // reference. Invalid generates debug output and zero result (if needed). + uint32_t merge_blk_id = TakeNextId(); + uint32_t valid_blk_id = TakeNextId(); + uint32_t invalid_blk_id = TakeNextId(); + std::unique_ptr merge_label(NewLabel(merge_blk_id)); + std::unique_ptr valid_label(NewLabel(valid_blk_id)); + std::unique_ptr invalid_label(NewLabel(invalid_blk_id)); + (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id, + merge_blk_id, SpvSelectionControlMaskNone); + // Gen valid branch + std::unique_ptr new_blk_ptr( + new BasicBlock(std::move(valid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder); + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + // Gen invalid block + new_blk_ptr.reset(new BasicBlock(std::move(invalid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Convert uptr from uint64 to 2 uint32 + Instruction* lo_uptr_inst = + builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id); + Instruction* rshift_uptr_inst = + builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id, + builder.GetUintConstantId(32)); + Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert, + rshift_uptr_inst->result_id()); + GenDebugStreamWrite( + uid2offset_[ref_inst->unique_id()], stage_idx, + {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()}, + &builder); + // Gen zero for invalid reference + uint32_t ref_type_id = ref_inst->type_id(); + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + // Gen merge block + new_blk_ptr.reset(new BasicBlock(std::move(merge_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Gen phi of new reference and zero, if necessary, and replace the + // result id of the original reference with that of the Phi. Kill original + // reference. + if (new_ref_id != 0) { + Instruction* phi_inst = builder.AddPhi( + ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id), + invalid_blk_id}); + context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id()); + } + new_blocks->push_back(std::move(new_blk_ptr)); + context()->KillInst(ref_inst); +} + +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 SpvOpTypeMatrix: + return type_inst->GetSingleWordInOperand(1) * + GetTypeLength(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypePointer: + assert(type_inst->GetSingleWordInOperand(0) == + SpvStorageClassPhysicalStorageBufferEXT && + "unexpected pointer type"); + return 8u; + default: + assert(false && "unexpected buffer reference type"); + return 0; + } +} + +void InstBuffAddrCheckPass::AddParam(uint32_t type_id, + std::vector* param_vec, + std::unique_ptr* input_func) { + uint32_t pid = TakeNextId(); + param_vec->push_back(pid); + std::unique_ptr param_inst(new Instruction( + get_module()->context(), SpvOpFunctionParameter, type_id, pid, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); + (*input_func)->AddParameter(std::move(param_inst)); +} + +uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() { + if (search_test_func_id_ == 0) { + // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)" + // which searches input buffer for buffer which most likely contains the + // pointer value |ref_ptr| and verifies that the entire reference of + // length |len| bytes is contained in the buffer. + search_test_func_id_ = TakeNextId(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + std::vector param_types = { + type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())}; + analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types); + analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); + std::unique_ptr func_inst( + new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(), + search_test_func_id_, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvFunctionControlMaskNone}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {type_mgr->GetTypeInstruction(reg_func_ty)}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); + std::unique_ptr input_func = + MakeUnique(std::move(func_inst)); + std::vector param_vec; + // Add ref_ptr and length parameters + AddParam(GetUint64Id(), ¶m_vec, &input_func); + AddParam(GetUintId(), ¶m_vec, &input_func); + // Empty first block. + uint32_t first_blk_id = TakeNextId(); + std::unique_ptr first_blk_label(NewLabel(first_blk_id)); + std::unique_ptr first_blk_ptr = + MakeUnique(std::move(first_blk_label)); + InstructionBuilder builder( + context(), &*first_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + uint32_t hdr_blk_id = TakeNextId(); + // Branch to search loop header + std::unique_ptr hdr_blk_label(NewLabel(hdr_blk_id)); + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpBranch, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}})); + first_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(first_blk_ptr)); + // Linear search loop header block + // TODO(greg-lunarg): Implement binary search + std::unique_ptr hdr_blk_ptr = + MakeUnique(std::move(hdr_blk_label)); + builder.SetInsertPoint(&*hdr_blk_ptr); + // Phi for search index. Starts with 1. + uint32_t cont_blk_id = TakeNextId(); + std::unique_ptr cont_blk_label(NewLabel(cont_blk_id)); + // Deal with def-use cycle caused by search loop index computation. + // Create Add and Phi instructions first, then do Def analysis on Add. + // Add Phi and Add instructions and do Use analysis later. + uint32_t idx_phi_id = TakeNextId(); + uint32_t idx_inc_id = TakeNextId(); + std::unique_ptr idx_inc_inst(new Instruction( + context(), SpvOpIAdd, GetUintId(), idx_inc_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {builder.GetUintConstantId(1u)}}})); + std::unique_ptr idx_phi_inst(new Instruction( + context(), SpvOpPhi, GetUintId(), idx_phi_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {builder.GetUintConstantId(1u)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}})); + get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst); + // Add (previously created) search index phi + (void)builder.AddInstruction(std::move(idx_phi_inst)); + // LoopMerge + uint32_t bound_test_blk_id = TakeNextId(); + std::unique_ptr bound_test_blk_label( + NewLabel(bound_test_blk_id)); + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpLoopMerge, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {bound_test_blk_id}}, + {SPV_OPERAND_TYPE_ID, {cont_blk_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}})); + // Branch to continue/work block + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpBranch, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}})); + hdr_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(hdr_blk_ptr)); + // Continue/Work Block. Read next buffer pointer and break if greater + // than ref_ptr arg. + std::unique_ptr cont_blk_ptr = + MakeUnique(std::move(cont_blk_label)); + builder.SetInsertPoint(&*cont_blk_ptr); + // Add (previously created) search index increment now. + (void)builder.AddInstruction(std::move(idx_inc_inst)); + // Load next buffer address from debug input buffer + uint32_t ibuf_id = GetInputBufferId(); + uint32_t ibuf_ptr_id = GetInputBufferPtrId(); + Instruction* uptr_ac_inst = builder.AddTernaryOp( + ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id); + uint32_t ibuf_type_id = GetInputBufferTypeId(); + Instruction* uptr_load_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id()); + // If loaded address greater than ref_ptr arg, break, else branch back to + // loop header + Instruction* uptr_test_inst = + builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan, + uptr_load_inst->result_id(), param_vec[0]); + (void)builder.AddConditionalBranch(uptr_test_inst->result_id(), + bound_test_blk_id, hdr_blk_id, + kInvalidId, SpvSelectionControlMaskNone); + cont_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(cont_blk_ptr)); + // Bounds test block. Read length of selected buffer and test that + // all len arg bytes are in buffer. + std::unique_ptr bound_test_blk_ptr = + MakeUnique(std::move(bound_test_blk_label)); + builder.SetInsertPoint(&*bound_test_blk_ptr); + // Decrement index to point to previous/candidate buffer address + Instruction* cand_idx_inst = builder.AddBinaryOp( + GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u)); + // Load candidate buffer address + Instruction* cand_ac_inst = + builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), + cand_idx_inst->result_id()); + Instruction* cand_load_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id()); + // Compute offset of ref_ptr from candidate buffer address + Instruction* offset_inst = builder.AddBinaryOp( + ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id()); + // Convert ref length to uint64 + Instruction* ref_len_64_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]); + // Add ref length to ref offset to compute end of reference + Instruction* ref_end_inst = + builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(), + ref_len_64_inst->result_id()); + // Load starting index of lengths in input buffer and convert to uint32 + Instruction* len_start_ac_inst = + builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), + builder.GetUintConstantId(0u)); + Instruction* len_start_load_inst = builder.AddUnaryOp( + ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id()); + Instruction* len_start_32_inst = builder.AddUnaryOp( + GetUintId(), SpvOpUConvert, len_start_load_inst->result_id()); + // Decrement search index to get candidate buffer length index + Instruction* cand_len_idx_inst = + builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(), + builder.GetUintConstantId(1u)); + // Add candidate length index to start index + Instruction* len_idx_inst = builder.AddBinaryOp( + GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(), + len_start_32_inst->result_id()); + // Load candidate buffer length + Instruction* len_ac_inst = + builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), + len_idx_inst->result_id()); + Instruction* len_load_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id()); + // Test if reference end within candidate buffer length + Instruction* len_test_inst = builder.AddBinaryOp( + GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(), + len_load_inst->result_id()); + // Return test result + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpReturnValue, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}})); + // Close block + bound_test_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(bound_test_blk_ptr)); + // Close function and add function to module + std::unique_ptr func_end_inst( + new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); + input_func->SetFunctionEnd(std::move(func_end_inst)); + context()->AddFunction(std::move(input_func)); + } + return search_test_func_id_; +} + +uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst, + InstructionBuilder* builder, + uint32_t* ref_uptr_id) { + // Enable Int64 if necessary + if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) { + std::unique_ptr cap_int64_inst(new Instruction( + context(), SpvOpCapability, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst); + get_module()->AddCapability(std::move(cap_int64_inst)); + } + // Convert reference pointer to uint64 + uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0); + Instruction* ref_uptr_inst = + builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id); + *ref_uptr_id = ref_uptr_inst->result_id(); + // Compute reference length in bytes + analysis::DefUseManager* du_mgr = get_def_use_mgr(); + Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id); + uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id(); + Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id); + uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1)); + uint32_t ref_len_id = builder->GetUintConstantId(ref_len); + // Gen call to search and test function + const std::vector args = {GetSearchAndTestFuncId(), *ref_uptr_id, + ref_len_id}; + Instruction* call_inst = + builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args); + uint32_t retval = call_inst->result_id(); + return retval; +} + +void InstBuffAddrCheckPass::GenBuffAddrCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + // Look for reference through indexed descriptor. If found, analyze and + // save components. If not, return. + Instruction* ref_inst = &*ref_inst_itr; + if (!IsPhysicalBuffAddrReference(ref_inst)) return; + // Move original block's preceding instructions into first new block + std::unique_ptr new_blk_ptr; + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_blocks->push_back(std::move(new_blk_ptr)); + uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef); + // Generate code to do search and test if all bytes of reference + // are within a listed buffer. Return reference pointer converted to uint64. + uint32_t ref_uptr_id; + uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id); + // Generate test of search results with true branch + // being full reference and false branch being debug output and zero + // for the referenced value. + GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst, + new_blocks); + // Move original block's remaining code into remainder/merge block and add + // to new blocks + BasicBlock* back_blk_ptr = &*new_blocks->back(); + MovePostludeCode(ref_block_itr, back_blk_ptr); +} + +void InstBuffAddrCheckPass::InitInstBuffAddrCheck() { + // Initialize base class + InitializeInstrument(); + // Initialize class + search_test_func_id_ = 0; +} + +Pass::Status InstBuffAddrCheckPass::ProcessImpl() { + // Perform bindless bounds check on each entry point function in module + InstProcessFunction pfn = + [this](BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx, + new_blocks); + }; + bool modified = InstProcessEntryPointCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status InstBuffAddrCheckPass::Process() { + if (!get_feature_mgr()->HasCapability( + SpvCapabilityPhysicalStorageBufferAddressesEXT)) + return Status::SuccessWithoutChange; + InitInstBuffAddrCheck(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h b/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h new file mode 100644 index 000000000..9ad3528ed --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h @@ -0,0 +1,133 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_ +#define LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_ + +#include "instrument_pass.h" + +namespace spvtools { +namespace opt { + +// This class/pass is designed to support the GPU-assisted validation layer of +// the Buffer Device Address (BDA) extension in +// https://github.com/KhronosGroup/Vulkan-ValidationLayers. The internal and +// external design of this class may change as the layer evolves. +class InstBuffAddrCheckPass : public InstrumentPass { + public: + // For test harness only + InstBuffAddrCheckPass() + : InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {} + // For all other interfaces + InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version) + : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr, + version) {} + + ~InstBuffAddrCheckPass() override = default; + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + 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. + uint32_t GetTypeLength(uint32_t type_id); + + // Add |type_id| param to |input_func| and add id to |param_vec|. + void AddParam(uint32_t type_id, std::vector* param_vec, + std::unique_ptr* input_func); + + // Return id for search and test function. Generate it if not already gen'd. + uint32_t GetSearchAndTestFuncId(); + + // Generate code into |builder| to do search of the BDA debug input buffer + // for the buffer used by |ref_inst| and test that all bytes of reference + // are within the buffer. Returns id of boolean value which is true if + // search and test is successful, false otherwise. + uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder, + uint32_t* ref_uptr_id); + + // This function does checking instrumentation on a single + // instruction which references through a physical storage buffer address. + // GenBuffAddrCheckCode generates code that checks that all bytes that + // are referenced fall within a buffer that was queried via + // the Vulkan API call vkGetBufferDeviceAddressEXT(). + // + // The function is designed to be passed to + // InstrumentPass::InstProcessEntryPointCallTree(), which applies the + // function to each instruction in a module and replaces the instruction + // with instrumented code if warranted. + // + // If |ref_inst_itr| is a physical storage buffer reference, return in + // |new_blocks| the result of instrumenting it with validation code within + // its block at |ref_block_itr|. The validation code first executes a check + // for the specific condition called for. If the check passes, it executes + // the remainder of the reference, otherwise writes a record to the debug + // output buffer stream including |function_idx, instruction_idx, stage_idx| + // and replaces the reference with the null value of the original type. The + // block at |ref_block_itr| can just be replaced with the blocks in + // |new_blocks|, which will contain at least two blocks. The last block will + // comprise all instructions following |ref_inst_itr|, + // preceded by a phi instruction if needed. + // + // This instrumentation function utilizes GenDebugStreamWrite() to write its + // error records. The validation-specific part of the error record will + // have the format: + // + // Validation Error Code (=kInstErrorBuffAddr) + // Buffer Address (lowest 32 bits) + // Buffer Address (highest 32 bits) + // + void GenBuffAddrCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks); + + // Return true if |ref_inst| is a physical buffer address reference, false + // otherwise. + bool IsPhysicalBuffAddrReference(Instruction* ref_inst); + + // Clone original reference |ref_inst| into |builder| and return id of result + uint32_t CloneOriginalReference(Instruction* ref_inst, + InstructionBuilder* builder); + + // Generate instrumentation code for boolean test result |check_id|, + // adding new blocks to |new_blocks|. Generate conditional branch to valid + // or invalid reference blocks. Generate valid reference block which does + // original reference |ref_inst|. Then generate invalid reference block which + // writes debug error output utilizing |ref_inst|, |error_id| and + // |stage_idx|. Generate merge block for valid and invalid reference blocks. + // Kill original reference. + void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id, + uint32_t stage_idx, Instruction* ref_inst, + std::vector>* new_blocks); + + // Initialize state for instrumenting physical buffer address checking + void InitInstBuffAddrCheck(); + + // Apply GenBuffAddrCheckCode to every instruction in module. + Pass::Status ProcessImpl(); + + // Id of search and test function, if already gen'd, else zero. + uint32_t search_test_func_id_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_ diff --git a/3rdparty/spirv-tools/source/opt/instrument_pass.cpp b/3rdparty/spirv-tools/source/opt/instrument_pass.cpp index 00bb918d0..418759b67 100644 --- a/3rdparty/spirv-tools/source/opt/instrument_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/instrument_pass.cpp @@ -107,7 +107,7 @@ void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id, builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id, builder->GetUintConstantId(field_offset)); uint32_t buf_id = GetOutputBufferId(); - uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); + uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); Instruction* achain_inst = builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, builder->GetUintConstantId(kDebugOutputDataOffset), @@ -373,19 +373,33 @@ void InstrumentPass::UpdateSucceedingPhis( }); } -// Return id for output buffer uint ptr type -uint32_t InstrumentPass::GetBufferUintPtrId() { - if (buffer_uint_ptr_id_ == 0) { - buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType( +uint32_t InstrumentPass::GetOutputBufferPtrId() { + if (output_buffer_ptr_id_ == 0) { + output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( GetUintId(), SpvStorageClassStorageBuffer); } - return buffer_uint_ptr_id_; + return output_buffer_ptr_id_; +} + +uint32_t InstrumentPass::GetInputBufferTypeId() { + return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id() + : GetUintId(); +} + +uint32_t InstrumentPass::GetInputBufferPtrId() { + if (input_buffer_ptr_id_ == 0) { + input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( + GetInputBufferTypeId(), SpvStorageClassStorageBuffer); + } + return input_buffer_ptr_id_; } uint32_t InstrumentPass::GetOutputBufferBinding() { switch (validation_id_) { case kInstValidationIdBindless: return kDebugOutputBindingStream; + case kInstValidationIdBuffAddr: + return kDebugOutputBindingStream; default: assert(false && "unexpected validation id"); } @@ -396,20 +410,24 @@ uint32_t InstrumentPass::GetInputBufferBinding() { switch (validation_id_) { case kInstValidationIdBindless: return kDebugInputBindingBindless; + case kInstValidationIdBuffAddr: + return kDebugInputBindingBuffAddr; default: assert(false && "unexpected validation id"); } return 0; } -analysis::Type* InstrumentPass::GetUintRuntimeArrayType( - analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) { - if (uint_rarr_ty_ == nullptr) { - analysis::Integer uint_ty(32, false); +analysis::Type* InstrumentPass::GetUintXRuntimeArrayType( + uint32_t width, analysis::Type** rarr_ty) { + if (*rarr_ty == nullptr) { + analysis::DecorationManager* deco_mgr = get_decoration_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint_ty(width, false); analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty); - uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp); - uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_); + *rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp); + uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty); // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of // a block, and will therefore be decorated with an ArrayStride. Therefore // the undecorated type returned here will not be pre-existing and can @@ -418,9 +436,16 @@ analysis::Type* InstrumentPass::GetUintRuntimeArrayType( // invalidated after this pass. assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 && "used RuntimeArray type returned"); - deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u); + deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, + width / 8u); } - return uint_rarr_ty_; + return *rarr_ty; +} + +analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) { + analysis::Type** rarr_ty = + (width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_; + return GetUintXRuntimeArrayType(width, rarr_ty); } void InstrumentPass::AddStorageBufferExt() { @@ -445,8 +470,7 @@ uint32_t InstrumentPass::GetOutputBufferId() { // If not created yet, create one analysis::DecorationManager* deco_mgr = get_decoration_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::Type* reg_uint_rarr_ty = - GetUintRuntimeArrayType(deco_mgr, type_mgr); + analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32); analysis::Integer uint_ty(32, false); analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty}); @@ -494,8 +518,8 @@ uint32_t InstrumentPass::GetInputBufferId() { // If not created yet, create one analysis::DecorationManager* deco_mgr = get_decoration_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::Type* reg_uint_rarr_ty = - GetUintRuntimeArrayType(deco_mgr, type_mgr); + uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u; + analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width); analysis::Struct buf_ty({reg_uint_rarr_ty}); analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); @@ -555,6 +579,16 @@ uint32_t InstrumentPass::GetUintId() { return uint_id_; } +uint32_t InstrumentPass::GetUint64Id() { + if (uint64_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint64_ty(64, false); + analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty); + uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty); + } + return uint64_id_; +} + uint32_t InstrumentPass::GetVecUintId(uint32_t len) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); analysis::Integer uint_ty(32, false); @@ -642,7 +676,7 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt; uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt; uint32_t buf_id = GetOutputBufferId(); - uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); + uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); Instruction* obuf_curr_sz_ac_inst = builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, builder.GetUintConstantId(kDebugOutputSizeOffset)); @@ -713,16 +747,17 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { uint32_t func_id = param2input_func_id_[param_cnt]; if (func_id != 0) return func_id; - // Create input function for param_cnt + // Create input function for param_cnt. func_id = TakeNextId(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); std::vector param_types; for (uint32_t c = 0; c < param_cnt; ++c) param_types.push_back(type_mgr->GetType(GetUintId())); - analysis::Function func_ty(type_mgr->GetType(GetUintId()), param_types); + uint32_t ibuf_type_id = GetInputBufferTypeId(); + analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types); analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); std::unique_ptr func_inst(new Instruction( - get_module()->context(), SpvOpFunction, GetUintId(), func_id, + get_module()->context(), SpvOpFunction, ibuf_type_id, func_id, {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvFunctionControlMaskNone}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, @@ -752,22 +787,27 @@ uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { // loaded value if it exists, and load value from input buffer at new offset. // Return last loaded value. uint32_t buf_id = GetInputBufferId(); - uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); + uint32_t buf_ptr_id = GetInputBufferPtrId(); uint32_t last_value_id = 0; for (uint32_t p = 0; p < param_cnt; ++p) { uint32_t offset_id; if (p == 0) { offset_id = param_vec[0]; } else { + if (ibuf_type_id != GetUintId()) { + Instruction* ucvt_inst = + builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id); + last_value_id = ucvt_inst->result_id(); + } Instruction* offset_inst = builder.AddBinaryOp( GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]); offset_id = offset_inst->result_id(); } Instruction* ac_inst = builder.AddTernaryOp( - buf_uint_ptr_id, SpvOpAccessChain, buf_id, + buf_ptr_id, SpvOpAccessChain, buf_id, builder.GetUintConstantId(kDebugInputDataOffset), offset_id); Instruction* load_inst = - builder.AddUnaryOp(GetUintId(), SpvOpLoad, ac_inst->result_id()); + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id()); last_value_id = load_inst->result_id(); } (void)builder.AddInstruction(MakeUnique( @@ -894,18 +934,21 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { void InstrumentPass::InitializeInstrument() { output_buffer_id_ = 0; - buffer_uint_ptr_id_ = 0; + output_buffer_ptr_id_ = 0; + input_buffer_ptr_id_ = 0; output_func_id_ = 0; output_func_param_cnt_ = 0; input_buffer_id_ = 0; v4float_id_ = 0; uint_id_ = 0; + uint64_id_ = 0; v4uint_id_ = 0; v3uint_id_ = 0; bool_id_ = 0; void_id_ = 0; storage_buffer_ext_defined_ = false; - uint_rarr_ty_ = nullptr; + uint32_rarr_ty_ = nullptr; + uint64_rarr_ty_ = nullptr; // clear collections id2function_.clear(); diff --git a/3rdparty/spirv-tools/source/opt/instrument_pass.h b/3rdparty/spirv-tools/source/opt/instrument_pass.h index 1b3f83e87..ead3b7306 100644 --- a/3rdparty/spirv-tools/source/opt/instrument_pass.h +++ b/3rdparty/spirv-tools/source/opt/instrument_pass.h @@ -60,6 +60,7 @@ namespace opt { // These are used to identify the general validation being done and map to // its output buffers. static const uint32_t kInstValidationIdBindless = 0; +static const uint32_t kInstValidationIdBuffAddr = 1; class InstrumentPass : public Pass { using cbb_ptr = const BasicBlock*; @@ -217,6 +218,9 @@ class InstrumentPass : public Pass { // Return id for 32-bit unsigned type uint32_t GetUintId(); + // Return id for 32-bit unsigned type + uint32_t GetUint64Id(); + // Return id for 32-bit unsigned type uint32_t GetBoolId(); @@ -224,11 +228,20 @@ class InstrumentPass : public Pass { uint32_t GetVoidId(); // Return pointer to type for runtime array of uint - analysis::Type* GetUintRuntimeArrayType(analysis::DecorationManager* deco_mgr, - analysis::TypeManager* type_mgr); + analysis::Type* GetUintXRuntimeArrayType(uint32_t width, + analysis::Type** rarr_ty); + + // Return pointer to type for runtime array of uint + analysis::Type* GetUintRuntimeArrayType(uint32_t width); // Return id for buffer uint type - uint32_t GetBufferUintPtrId(); + uint32_t GetOutputBufferPtrId(); + + // Return id for buffer uint type + uint32_t GetInputBufferTypeId(); + + // Return id for buffer uint type + uint32_t GetInputBufferPtrId(); // Return binding for output buffer for current validation. uint32_t GetOutputBufferBinding(); @@ -354,8 +367,11 @@ class InstrumentPass : public Pass { // id for output buffer variable uint32_t output_buffer_id_; - // type id for output buffer element - uint32_t buffer_uint_ptr_id_; + // ptr type id for output buffer element + uint32_t output_buffer_ptr_id_; + + // ptr type id for input buffer element + uint32_t input_buffer_ptr_id_; // id for debug output function uint32_t output_func_id_; @@ -381,6 +397,9 @@ class InstrumentPass : public Pass { // id for 32-bit unsigned type uint32_t uint_id_; + // id for 32-bit unsigned type + uint32_t uint64_id_; + // id for bool type uint32_t bool_id_; @@ -394,7 +413,10 @@ class InstrumentPass : public Pass { bool storage_buffer_ext_defined_; // runtime array of uint type - analysis::Type* uint_rarr_ty_; + analysis::Type* uint64_rarr_ty_; + + // runtime array of uint type + analysis::Type* uint32_rarr_ty_; // Pre-instrumentation same-block insts std::unordered_map same_block_pre_; diff --git a/3rdparty/spirv-tools/source/opt/ir_builder.h b/3rdparty/spirv-tools/source/opt/ir_builder.h index da7405512..f12dc95d1 100644 --- a/3rdparty/spirv-tools/source/opt/ir_builder.h +++ b/3rdparty/spirv-tools/source/opt/ir_builder.h @@ -465,6 +465,23 @@ class InstructionBuilder { return AddInstruction(std::move(new_inst)); } + Instruction* AddFunctionCall(uint32_t result_type, uint32_t function, + const std::vector& parameters) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {function}}); + for (uint32_t id : parameters) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr new_inst(new Instruction( + GetContext(), SpvOpFunctionCall, result_type, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + // Inserts the new instruction before the insertion point. Instruction* AddInstruction(std::unique_ptr&& insn) { Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn)); @@ -512,6 +529,10 @@ class InstructionBuilder { // Returns true if the users requested to update |analysis|. inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const { + if (!GetContext()->AreAnalysesValid(analysis)) { + // Do not try to update something that is not built. + return false; + } return preserved_analyses_ & analysis; } diff --git a/3rdparty/spirv-tools/source/opt/optimizer.cpp b/3rdparty/spirv-tools/source/opt/optimizer.cpp index f3b56c314..cbdda2d92 100644 --- a/3rdparty/spirv-tools/source/opt/optimizer.cpp +++ b/3rdparty/spirv-tools/source/opt/optimizer.cpp @@ -107,8 +107,10 @@ Optimizer& Optimizer::RegisterPass(PassToken&& p) { // or enable more copy propagation. Optimizer& Optimizer::RegisterLegalizationPasses() { return - // Remove unreachable block so that merge return works. - RegisterPass(CreateDeadBranchElimPass()) + // Wrap OpKill instructions so all other code can be inlined. + RegisterPass(CreateWrapOpKillPass()) + // Remove unreachable block so that merge return works. + .RegisterPass(CreateDeadBranchElimPass()) // Merge the returns so we can inline. .RegisterPass(CreateMergeReturnPass()) // Make sure uses and definitions are in the same function. @@ -154,7 +156,8 @@ Optimizer& Optimizer::RegisterLegalizationPasses() { } Optimizer& Optimizer::RegisterPerformancePasses() { - return RegisterPass(CreateDeadBranchElimPass()) + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) .RegisterPass(CreateInlineExhaustivePass()) .RegisterPass(CreateAggressiveDCEPass()) @@ -190,7 +193,8 @@ Optimizer& Optimizer::RegisterPerformancePasses() { } Optimizer& Optimizer::RegisterSizePasses() { - return RegisterPass(CreateDeadBranchElimPass()) + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) .RegisterPass(CreateInlineExhaustivePass()) .RegisterPass(CreateAggressiveDCEPass()) @@ -396,11 +400,20 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "replace-invalid-opcode") { RegisterPass(CreateReplaceInvalidOpcodePass()); } else if (pass_name == "inst-bindless-check") { + RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2)); + RegisterPass(CreateSimplificationPass()); + RegisterPass(CreateDeadBranchElimPass()); + RegisterPass(CreateBlockMergePass()); + RegisterPass(CreateAggressiveDCEPass()); + } else if (pass_name == "inst-desc-idx-check") { RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); RegisterPass(CreateAggressiveDCEPass()); + } else if (pass_name == "inst-buff-addr-check") { + RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2)); + RegisterPass(CreateAggressiveDCEPass()); } else if (pass_name == "simplify-instructions") { RegisterPass(CreateSimplificationPass()); } else if (pass_name == "ssa-rewrite") { @@ -477,6 +490,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateDecomposeInitializedVariablesPass()); } else if (pass_name == "graphics-robust-access") { RegisterPass(CreateGraphicsRobustAccessPass()); + } else if (pass_name == "wrap-opkill") { + RegisterPass(CreateWrapOpKillPass()); } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -853,6 +868,13 @@ Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, input_init_enable, version)); } +Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, + uint32_t shader_id, + uint32_t version) { + return MakeUnique( + MakeUnique(desc_set, shader_id, version)); +} + Optimizer::PassToken CreateCodeSinkingPass() { return MakeUnique( MakeUnique()); @@ -893,4 +915,8 @@ Optimizer::PassToken CreateDescriptorScalarReplacementPass() { MakeUnique()); } +Optimizer::PassToken CreateWrapOpKillPass() { + return MakeUnique(MakeUnique()); +} + } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/pass.cpp b/3rdparty/spirv-tools/source/opt/pass.cpp index a783f4f95..f9e4a5d47 100644 --- a/3rdparty/spirv-tools/source/opt/pass.cpp +++ b/3rdparty/spirv-tools/source/opt/pass.cpp @@ -43,7 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) { if (status == Status::SuccessWithChange) { ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses()); } - assert(ctx->IsConsistent()); + assert((status == Status::Failure || ctx->IsConsistent()) && + "An analysis in the context is out of date."); return status; } diff --git a/3rdparty/spirv-tools/source/opt/passes.h b/3rdparty/spirv-tools/source/opt/passes.h index 86588f7eb..1dede0ef2 100644 --- a/3rdparty/spirv-tools/source/opt/passes.h +++ b/3rdparty/spirv-tools/source/opt/passes.h @@ -43,6 +43,7 @@ #include "source/opt/inline_exhaustive_pass.h" #include "source/opt/inline_opaque_pass.h" #include "source/opt/inst_bindless_check_pass.h" +#include "source/opt/inst_buff_addr_check_pass.h" #include "source/opt/legalize_vector_shuffle_pass.h" #include "source/opt/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" @@ -76,5 +77,6 @@ #include "source/opt/upgrade_memory_model.h" #include "source/opt/vector_dce.h" #include "source/opt/workaround1209.h" +#include "source/opt/wrap_opkill.h" #endif // SOURCE_OPT_PASSES_H_ diff --git a/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp b/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp index d41d8f217..6df690dc0 100644 --- a/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp @@ -58,7 +58,9 @@ Pass::Status PrivateToLocalPass::Process() { modified = !variables_to_move.empty(); for (auto p : variables_to_move) { - MoveVariable(p.first, p.second); + if (!MoveVariable(p.first, p.second)) { + return Status::Failure; + } localized_variables.insert(p.first->result_id()); } @@ -112,7 +114,7 @@ Function* PrivateToLocalPass::FindLocalFunction(const Instruction& inst) const { return target_function; } // namespace opt -void PrivateToLocalPass::MoveVariable(Instruction* variable, +bool PrivateToLocalPass::MoveVariable(Instruction* variable, Function* function) { // The variable needs to be removed from the global section, and placed in the // header of the function. First step remove from the global list. @@ -125,6 +127,9 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable, // Update the type as well. uint32_t new_type_id = GetNewType(variable->type_id()); + if (new_type_id == 0) { + return false; + } variable->SetResultType(new_type_id); // Place the variable at the start of the first basic block. @@ -133,7 +138,7 @@ void PrivateToLocalPass::MoveVariable(Instruction* variable, function->begin()->begin()->InsertBefore(move(var)); // Update uses where the type may have changed. - UpdateUses(variable->result_id()); + return UpdateUses(variable->result_id()); } uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) { @@ -143,7 +148,9 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) { old_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx); uint32_t new_type_id = type_mgr->FindPointerToType(pointee_type_id, SpvStorageClassFunction); - context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id)); + if (new_type_id != 0) { + context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id)); + } return new_type_id; } @@ -168,7 +175,7 @@ bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const { } } -void PrivateToLocalPass::UpdateUse(Instruction* inst) { +bool PrivateToLocalPass::UpdateUse(Instruction* inst) { // 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. @@ -179,14 +186,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) { // The type is fine because it is the type pointed to, and that does not // change. break; - case SpvOpAccessChain: + case SpvOpAccessChain: { context()->ForgetUses(inst); - inst->SetResultType(GetNewType(inst->type_id())); + uint32_t new_type_id = GetNewType(inst->type_id()); + if (new_type_id == 0) { + return false; + } + inst->SetResultType(new_type_id); context()->AnalyzeUses(inst); // Update uses where the type may have changed. - UpdateUses(inst->result_id()); - break; + if (!UpdateUses(inst->result_id())) { + return false; + } + } break; case SpvOpName: case SpvOpEntryPoint: // entry points will be updated separately. break; @@ -195,15 +208,20 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) { "Do not know how to update the type for this instruction."); break; } + return true; } -void PrivateToLocalPass::UpdateUses(uint32_t id) { + +bool PrivateToLocalPass::UpdateUses(uint32_t id) { std::vector uses; context()->get_def_use_mgr()->ForEachUser( id, [&uses](Instruction* use) { uses.push_back(use); }); for (Instruction* use : uses) { - UpdateUse(use); + if (!UpdateUse(use)) { + return false; + } } + return true; } } // namespace opt diff --git a/3rdparty/spirv-tools/source/opt/private_to_local_pass.h b/3rdparty/spirv-tools/source/opt/private_to_local_pass.h index 467853030..3f9135c09 100644 --- a/3rdparty/spirv-tools/source/opt/private_to_local_pass.h +++ b/3rdparty/spirv-tools/source/opt/private_to_local_pass.h @@ -41,8 +41,8 @@ class PrivateToLocalPass : public Pass { private: // Moves |variable| from the private storage class to the function storage - // class of |function|. - void MoveVariable(Instruction* variable, Function* function); + // 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 // referenced in a single function and all of uses are valid as defined by @@ -58,13 +58,13 @@ class PrivateToLocalPass : public Pass { // Given the result id of a pointer type, |old_type_id|, this function // returns the id of a the same pointer type except the storage class has // been changed to function. If the type does not already exist, it will be - // created. + // created. Returns 0 if the new type could not be found or generated. uint32_t GetNewType(uint32_t old_type_id); // Updates |inst|, and any instruction dependent on |inst|, to reflect the // change of the base pointer now pointing to the function storage class. - void UpdateUse(Instruction* inst); - void UpdateUses(uint32_t id); + bool UpdateUse(Instruction* inst); + bool UpdateUses(uint32_t id); }; } // namespace opt diff --git a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp index d100cb05b..d748e7f2f 100644 --- a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp @@ -78,36 +78,47 @@ Pass::Status ScalarReplacementPass::ReplaceVariable( } std::vector dead; - if (get_def_use_mgr()->WhileEachUser( - inst, [this, &replacements, &dead](Instruction* user) { - if (!IsAnnotationInst(user->opcode())) { - switch (user->opcode()) { - case SpvOpLoad: - ReplaceWholeLoad(user, replacements); - dead.push_back(user); - break; - case SpvOpStore: - ReplaceWholeStore(user, replacements); - dead.push_back(user); - break; - case SpvOpAccessChain: - case SpvOpInBoundsAccessChain: - if (ReplaceAccessChain(user, replacements)) - dead.push_back(user); - else - return false; - break; - case SpvOpName: - case SpvOpMemberName: - break; - default: - assert(false && "Unexpected opcode"); - break; + bool replaced_all_uses = get_def_use_mgr()->WhileEachUser( + inst, [this, &replacements, &dead](Instruction* user) { + if (!IsAnnotationInst(user->opcode())) { + switch (user->opcode()) { + case SpvOpLoad: + if (ReplaceWholeLoad(user, replacements)) { + dead.push_back(user); + } else { + return false; } - } - return true; - })) + break; + case SpvOpStore: + if (ReplaceWholeStore(user, replacements)) { + dead.push_back(user); + } else { + return false; + } + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (ReplaceAccessChain(user, replacements)) + dead.push_back(user); + else + return false; + break; + case SpvOpName: + case SpvOpMemberName: + break; + default: + assert(false && "Unexpected opcode"); + break; + } + } + return true; + }); + + if (replaced_all_uses) { dead.push_back(inst); + } else { + return Status::Failure; + } // If there are no dead instructions to clean up, return with no changes. if (dead.empty()) return Status::SuccessWithoutChange; @@ -133,7 +144,7 @@ Pass::Status ScalarReplacementPass::ReplaceVariable( return Status::SuccessWithChange; } -void ScalarReplacementPass::ReplaceWholeLoad( +bool ScalarReplacementPass::ReplaceWholeLoad( Instruction* load, const std::vector& replacements) { // Replaces the load of the entire composite with a load from each replacement // variable followed by a composite construction. @@ -150,6 +161,9 @@ void ScalarReplacementPass::ReplaceWholeLoad( Instruction* type = GetStorageType(var); uint32_t loadId = TakeNextId(); + if (loadId == 0) { + return false; + } std::unique_ptr newLoad( new Instruction(context(), SpvOpLoad, type->result_id(), loadId, std::initializer_list{ @@ -168,6 +182,9 @@ void ScalarReplacementPass::ReplaceWholeLoad( // Construct a new composite. uint32_t compositeId = TakeNextId(); + if (compositeId == 0) { + return false; + } where = load; std::unique_ptr compositeConstruct(new Instruction( context(), SpvOpCompositeConstruct, load->type_id(), compositeId, {})); @@ -180,9 +197,10 @@ void ScalarReplacementPass::ReplaceWholeLoad( get_def_use_mgr()->AnalyzeInstDefUse(&*where); context()->set_instr_block(&*where, block); context()->ReplaceAllUsesWith(load->result_id(), compositeId); + return true; } -void ScalarReplacementPass::ReplaceWholeStore( +bool ScalarReplacementPass::ReplaceWholeStore( Instruction* store, const std::vector& replacements) { // Replaces a store to the whole composite with a series of extract and stores // to each element. @@ -199,6 +217,9 @@ void ScalarReplacementPass::ReplaceWholeStore( Instruction* type = GetStorageType(var); uint32_t extractId = TakeNextId(); + if (extractId == 0) { + return false; + } std::unique_ptr extract(new Instruction( context(), SpvOpCompositeExtract, type->result_id(), extractId, std::initializer_list{ @@ -224,6 +245,7 @@ void ScalarReplacementPass::ReplaceWholeStore( get_def_use_mgr()->AnalyzeInstDefUse(&*iter); context()->set_instr_block(&*iter, block); } + return true; } bool ScalarReplacementPass::ReplaceAccessChain( @@ -247,6 +269,9 @@ bool ScalarReplacementPass::ReplaceAccessChain( // Replace input access chain with another access chain. BasicBlock::iterator chainIter(chain); uint32_t replacementId = TakeNextId(); + if (replacementId == 0) { + return false; + } std::unique_ptr replacementChain(new Instruction( context(), chain->opcode(), chain->type_id(), replacementId, std::initializer_list{ @@ -329,6 +354,10 @@ void ScalarReplacementPass::TransferAnnotations( if (decoration == SpvDecorationInvariant || decoration == SpvDecorationRestrict) { for (auto var : *replacements) { + if (var == nullptr) { + continue; + } + std::unique_ptr annotation( new Instruction(context(), SpvOpDecorate, 0, 0, std::initializer_list{ @@ -350,6 +379,11 @@ void ScalarReplacementPass::CreateVariable( std::vector* replacements) { uint32_t ptrId = GetOrCreatePointerType(typeId); uint32_t id = TakeNextId(); + + if (id == 0) { + replacements->push_back(nullptr); + } + std::unique_ptr variable(new Instruction( context(), SpvOpVariable, ptrId, id, std::initializer_list{ @@ -671,44 +705,51 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst) const { bool ScalarReplacementPass::CheckUses(const Instruction* inst, VariableStats* stats) const { + uint64_t max_legal_index = GetMaxLegalIndex(inst); + bool ok = true; - get_def_use_mgr()->ForEachUse( - inst, [this, stats, &ok](const Instruction* user, uint32_t index) { - // Annotations are check as a group separately. - if (!IsAnnotationInst(user->opcode())) { - switch (user->opcode()) { - case SpvOpAccessChain: - case SpvOpInBoundsAccessChain: - if (index == 2u && user->NumInOperands() > 1) { - uint32_t id = user->GetSingleWordInOperand(1u); - const Instruction* opInst = get_def_use_mgr()->GetDef(id); - if (!IsCompileTimeConstantInst(opInst->opcode())) { - ok = false; - } else { - if (!CheckUsesRelaxed(user)) ok = false; - } - stats->num_partial_accesses++; - } else { - ok = false; - } - break; - case SpvOpLoad: - if (!CheckLoad(user, index)) ok = false; - stats->num_full_accesses++; - break; - case SpvOpStore: - if (!CheckStore(user, index)) ok = false; - stats->num_full_accesses++; - break; - case SpvOpName: - case SpvOpMemberName: - break; - default: + get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok]( + const Instruction* user, + uint32_t index) { + // Annotations are check as a group separately. + if (!IsAnnotationInst(user->opcode())) { + switch (user->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (index == 2u && user->NumInOperands() > 1) { + uint32_t id = user->GetSingleWordInOperand(1u); + const Instruction* opInst = get_def_use_mgr()->GetDef(id); + const auto* constant = + context()->get_constant_mgr()->GetConstantFromInst(opInst); + if (!constant) { ok = false; - break; + } else if (constant->GetZeroExtendedValue() >= max_legal_index) { + ok = false; + } else { + if (!CheckUsesRelaxed(user)) ok = false; + } + stats->num_partial_accesses++; + } else { + ok = false; } - } - }); + break; + case SpvOpLoad: + if (!CheckLoad(user, index)) ok = false; + stats->num_full_accesses++; + break; + case SpvOpStore: + if (!CheckStore(user, index)) ok = false; + stats->num_full_accesses++; + break; + case SpvOpName: + case SpvOpMemberName: + break; + default: + ok = false; + break; + } + } + }); return ok; } @@ -838,5 +879,24 @@ Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) { return null_inst; } +uint64_t ScalarReplacementPass::GetMaxLegalIndex( + const Instruction* var_inst) const { + assert(var_inst->opcode() == SpvOpVariable && + "|var_inst| must be a variable instruction."); + Instruction* type = GetStorageType(var_inst); + switch (type->opcode()) { + case SpvOpTypeStruct: + return type->NumInOperands(); + case SpvOpTypeArray: + return GetArrayLength(type); + case SpvOpTypeMatrix: + case SpvOpTypeVector: + return GetNumElements(type); + default: + return 0; + } + return 0; +} + } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h index 5b5198155..e20f1f1a5 100644 --- a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h +++ b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h @@ -143,7 +143,8 @@ class ScalarReplacementPass : public Pass { bool CheckStore(const Instruction* inst, uint32_t index) const; // Creates a variable of type |typeId| from the |index|'th element of - // |varInst|. The new variable is added to |replacements|. + // |varInst|. The new variable is added to |replacements|. If the variable + // could not be created, then |nullptr| is appended to |replacements|. void CreateVariable(uint32_t typeId, Instruction* varInst, uint32_t index, std::vector* replacements); @@ -187,21 +188,21 @@ class ScalarReplacementPass : public Pass { // Generates a load for each replacement variable and then creates a new // composite by combining all of the loads. // - // |load| must be a load. - void ReplaceWholeLoad(Instruction* load, + // |load| must be a load. Returns true if successful. + bool ReplaceWholeLoad(Instruction* load, const std::vector& replacements); // Replaces the store to the entire composite. // // Generates a composite extract and store for each element in the scalarized - // variable from the original store data input. - void ReplaceWholeStore(Instruction* store, + // variable from the original store data input. Returns true if successful. + bool ReplaceWholeStore(Instruction* store, const std::vector& replacements); // Replaces an access chain to the composite variable with either a direct use // of the appropriate replacement variable or another access chain with the - // replacement variable as the base and one fewer indexes. Returns false if - // the chain has an out of bounds access. + // replacement variable as the base and one fewer indexes. Returns true if + // successful. bool ReplaceAccessChain(Instruction* chain, const std::vector& replacements); @@ -222,10 +223,16 @@ class ScalarReplacementPass : public Pass { // Maps type id to OpConstantNull for that type. std::unordered_map type_to_null_; + // Returns the number of elements in the variable |var_inst|. + uint64_t GetMaxLegalIndex(const Instruction* var_inst) const; + + // Returns true if |length| is larger than limit on the size of the variable + // that we will be willing to split. + bool IsLargerThanSizeLimit(uint64_t length) const; + // Limit on the number of members in an object that will be replaced. // 0 means there is no limit. uint32_t max_num_elements_; - bool IsLargerThanSizeLimit(uint64_t length) const; char name_[55]; }; diff --git a/3rdparty/spirv-tools/source/opt/type_manager.cpp b/3rdparty/spirv-tools/source/opt/type_manager.cpp index 1c27b16f4..d34948194 100644 --- a/3rdparty/spirv-tools/source/opt/type_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/type_manager.cpp @@ -213,6 +213,10 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { std::unique_ptr typeInst; // TODO(1841): Handle id overflow. id = context()->TakeNextId(); + if (id == 0) { + return 0; + } + RegisterType(id, *type); switch (type->kind()) { #define DefineParameterlessCase(kind) \ @@ -247,6 +251,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { break; case Type::kVector: { uint32_t subtype = GetTypeInstruction(type->AsVector()->element_type()); + if (subtype == 0) { + return 0; + } typeInst = MakeUnique(context(), SpvOpTypeVector, 0, id, std::initializer_list{ @@ -257,6 +264,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { } case Type::kMatrix: { uint32_t subtype = GetTypeInstruction(type->AsMatrix()->element_type()); + if (subtype == 0) { + return 0; + } typeInst = MakeUnique(context(), SpvOpTypeMatrix, 0, id, std::initializer_list{ @@ -268,6 +278,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { case Type::kImage: { const Image* image = type->AsImage(); uint32_t subtype = GetTypeInstruction(image->sampled_type()); + if (subtype == 0) { + return 0; + } typeInst = MakeUnique( context(), SpvOpTypeImage, 0, id, std::initializer_list{ @@ -289,6 +302,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { case Type::kSampledImage: { uint32_t subtype = GetTypeInstruction(type->AsSampledImage()->image_type()); + if (subtype == 0) { + return 0; + } typeInst = MakeUnique( context(), SpvOpTypeSampledImage, 0, id, std::initializer_list{{SPV_OPERAND_TYPE_ID, {subtype}}}); @@ -296,6 +312,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { } case Type::kArray: { uint32_t subtype = GetTypeInstruction(type->AsArray()->element_type()); + if (subtype == 0) { + return 0; + } typeInst = MakeUnique( context(), SpvOpTypeArray, 0, id, std::initializer_list{ @@ -306,6 +325,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { case Type::kRuntimeArray: { uint32_t subtype = GetTypeInstruction(type->AsRuntimeArray()->element_type()); + if (subtype == 0) { + return 0; + } typeInst = MakeUnique( context(), SpvOpTypeRuntimeArray, 0, id, std::initializer_list{{SPV_OPERAND_TYPE_ID, {subtype}}}); @@ -315,7 +337,11 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { std::vector ops; const Struct* structTy = type->AsStruct(); for (auto ty : structTy->element_types()) { - ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)})); + uint32_t member_type_id = GetTypeInstruction(ty); + if (member_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {member_type_id})); } typeInst = MakeUnique(context(), SpvOpTypeStruct, 0, id, ops); @@ -337,6 +363,9 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { case Type::kPointer: { const Pointer* pointer = type->AsPointer(); uint32_t subtype = GetTypeInstruction(pointer->pointee_type()); + if (subtype == 0) { + return 0; + } typeInst = MakeUnique( context(), SpvOpTypePointer, 0, id, std::initializer_list{ @@ -348,10 +377,17 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { case Type::kFunction: { std::vector ops; const Function* function = type->AsFunction(); - ops.push_back(Operand(SPV_OPERAND_TYPE_ID, - {GetTypeInstruction(function->return_type())})); + uint32_t return_type_id = GetTypeInstruction(function->return_type()); + if (return_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {return_type_id})); for (auto ty : function->param_types()) { - ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {GetTypeInstruction(ty)})); + uint32_t paramater_type_id = GetTypeInstruction(ty); + if (paramater_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {paramater_type_id})); } typeInst = MakeUnique(context(), SpvOpTypeFunction, 0, id, ops); @@ -594,6 +630,9 @@ void TypeManager::RegisterType(uint32_t id, const Type& type) { Type* TypeManager::GetRegisteredType(const Type* type) { uint32_t id = GetTypeInstruction(type); + if (id == 0) { + return nullptr; + } return GetType(id); } diff --git a/3rdparty/spirv-tools/source/opt/type_manager.h b/3rdparty/spirv-tools/source/opt/type_manager.h index ecc7858fe..bec72d2fc 100644 --- a/3rdparty/spirv-tools/source/opt/type_manager.h +++ b/3rdparty/spirv-tools/source/opt/type_manager.h @@ -101,7 +101,8 @@ class TypeManager { std::pair> GetTypeAndPointerType( uint32_t id, SpvStorageClass sc) const; - // Returns an id for a declaration representing |type|. + // Returns an id for a declaration representing |type|. Returns 0 if the type + // does not exists, and could not be generated. // // If |type| is registered, then the registered id is returned. Otherwise, // this function recursively adds type and annotation instructions as @@ -109,7 +110,8 @@ class TypeManager { uint32_t GetTypeInstruction(const Type* type); // Find pointer to type and storage in module, return its resultId. If it is - // not found, a new type is created, and its id is returned. + // not found, a new type is created, and its id is returned. Returns 0 if the + // type could not be created. uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class); // Registers |id| to |type|. @@ -118,6 +120,7 @@ class TypeManager { // unchanged. void RegisterType(uint32_t id, const Type& type); + // Return the registered type object that is the same as |type|. Type* GetRegisteredType(const Type* type); // Removes knowledge of |id| from the manager. diff --git a/3rdparty/spirv-tools/source/opt/types.cpp b/3rdparty/spirv-tools/source/opt/types.cpp index 8e2023856..4f7150fc3 100644 --- a/3rdparty/spirv-tools/source/opt/types.cpp +++ b/3rdparty/spirv-tools/source/opt/types.cpp @@ -571,10 +571,10 @@ void Pointer::GetExtraHashWords(std::vector* words, void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; } -Function::Function(Type* ret_type, const std::vector& params) +Function::Function(const Type* ret_type, const std::vector& params) : Type(kFunction), return_type_(ret_type), param_types_(params) {} -Function::Function(Type* ret_type, std::vector& params) +Function::Function(const Type* ret_type, std::vector& params) : Type(kFunction), return_type_(ret_type), param_types_(params) {} bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const { diff --git a/3rdparty/spirv-tools/source/opt/types.h b/3rdparty/spirv-tools/source/opt/types.h index 1d3552ac3..57920df96 100644 --- a/3rdparty/spirv-tools/source/opt/types.h +++ b/3rdparty/spirv-tools/source/opt/types.h @@ -520,8 +520,8 @@ class Pointer : public Type { class Function : public Type { public: - Function(Type* ret_type, const std::vector& params); - Function(Type* ret_type, std::vector& params); + Function(const Type* ret_type, const std::vector& params); + Function(const Type* ret_type, std::vector& params); Function(const Function&) = default; std::string str() const override; diff --git a/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp new file mode 100644 index 000000000..d10cdd28e --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/wrap_opkill.h" + +#include "ir_builder.h" + +namespace spvtools { +namespace opt { + +Pass::Status WrapOpKill::Process() { + bool modified = false; + + for (auto& func : *get_module()) { + bool successful = func.WhileEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == SpvOpKill) { + modified = true; + if (!ReplaceWithFunctionCall(inst)) { + return false; + } + } + return true; + }); + + if (!successful) { + return Status::Failure; + } + } + + if (opkill_function_ != nullptr) { + assert(modified && + "The function should only be generated if something was modified."); + context()->AddFunction(std::move(opkill_function_)); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) { + assert(inst->opcode() == SpvOpKill && + "|inst| must be an OpKill instruction."); + InstructionBuilder ir_builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + uint32_t func_id = GetOpKillFuncId(); + if (func_id == 0) { + return false; + } + if (ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}) == nullptr) { + return false; + } + ir_builder.AddUnreachable(); + context()->KillInst(inst); + return true; +} + +uint32_t WrapOpKill::GetVoidTypeId() { + if (void_type_id_ != 0) { + return void_type_id_; + } + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + void_type_id_ = type_mgr->GetTypeInstruction(&void_type); + return void_type_id_; +} + +uint32_t WrapOpKill::GetVoidFunctionTypeId() { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + const analysis::Type* registered_void_type = + type_mgr->GetRegisteredType(&void_type); + + analysis::Function func_type(registered_void_type, {}); + return type_mgr->GetTypeInstruction(&func_type); +} + +uint32_t WrapOpKill::GetOpKillFuncId() { + if (opkill_function_ != nullptr) { + return opkill_function_->result_id(); + } + + uint32_t opkill_func_id = TakeNextId(); + if (opkill_func_id == 0) { + return 0; + } + + // Generate the function start instruction + std::unique_ptr func_start(new Instruction( + context(), SpvOpFunction, GetVoidTypeId(), opkill_func_id, {})); + func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}}); + func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}}); + opkill_function_.reset(new Function(std::move(func_start))); + + // Generate the function end instruction + std::unique_ptr func_end( + new Instruction(context(), SpvOpFunctionEnd, 0, 0, {})); + opkill_function_->SetFunctionEnd(std::move(func_end)); + + // Create the one basic block for the function. + uint32_t lab_id = TakeNextId(); + if (lab_id == 0) { + return 0; + } + std::unique_ptr label_inst( + new Instruction(context(), SpvOpLabel, 0, lab_id, {})); + std::unique_ptr bb(new BasicBlock(std::move(label_inst))); + + // Add the OpKill to the basic block + std::unique_ptr kill_inst( + new Instruction(context(), SpvOpKill, 0, 0, {})); + bb->AddInstruction(std::move(kill_inst)); + + // Add the bb to the function + opkill_function_->AddBasicBlock(std::move(bb)); + + // Add the function to the module. + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + opkill_function_->ForEachInst( + [this](Instruction* inst) { context()->AnalyzeDefUse(inst); }); + } + + if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { + for (BasicBlock& basic_block : *opkill_function_) { + context()->set_instr_block(basic_block.GetLabelInst(), &basic_block); + for (Instruction& inst : basic_block) { + context()->set_instr_block(&inst, &basic_block); + } + } + } + + return opkill_function_->result_id(); +} + +} // namespace opt +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/wrap_opkill.h b/3rdparty/spirv-tools/source/opt/wrap_opkill.h new file mode 100644 index 000000000..8b0328132 --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/wrap_opkill.h @@ -0,0 +1,72 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_WRAP_OPKILL_H_ +#define SOURCE_OPT_WRAP_OPKILL_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class WrapOpKill : public Pass { + public: + WrapOpKill() : void_type_id_(0) {} + + const char* name() const override { return "wrap-opkill"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Replaces the OpKill instruction |inst| with a function call to a function + // that contains a single instruction, which is OpKill. An OpUnreachable + // instruction will be placed after the function call. Return true if + // successful. + bool ReplaceWithFunctionCall(Instruction* inst); + + // Returns the id of the void type. + uint32_t GetVoidTypeId(); + + // Returns the id of the function type for a void function with no parameters. + uint32_t GetVoidFunctionTypeId(); + + // Return the id of a function that has return type void, has no parameters, + // and contains a single instruction, which is an OpKill. Returns 0 if the + // function could not be generated. + uint32_t GetOpKillFuncId(); + + // The id of the void type. If its value is 0, then the void type has not + // been found or created yet. + uint32_t void_type_id_; + + // The function that is a single instruction, which is an OpKill. The + // function has a void return type and takes no parameters. If the function is + // |nullptr|, then the function has not been generated. + std::unique_ptr opkill_function_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_WRAP_OPKILL_H_ diff --git a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt index def4d2104..7651e8664 100644 --- a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt @@ -30,6 +30,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_function_reduction_opportunity.h remove_function_reduction_opportunity_finder.h remove_opname_instruction_reduction_opportunity_finder.h + remove_relaxed_precision_decoration_opportunity_finder.h remove_selection_reduction_opportunity.h remove_selection_reduction_opportunity_finder.h remove_unreferenced_instruction_reduction_opportunity_finder.h @@ -56,6 +57,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_function_reduction_opportunity.cpp remove_function_reduction_opportunity_finder.cpp remove_instruction_reduction_opportunity.cpp + remove_relaxed_precision_decoration_opportunity_finder.cpp remove_selection_reduction_opportunity.cpp remove_selection_reduction_opportunity_finder.cpp remove_unreferenced_instruction_reduction_opportunity_finder.cpp diff --git a/3rdparty/spirv-tools/source/reduce/reducer.cpp b/3rdparty/spirv-tools/source/reduce/reducer.cpp index a677be35b..ebb5d471e 100644 --- a/3rdparty/spirv-tools/source/reduce/reducer.cpp +++ b/3rdparty/spirv-tools/source/reduce/reducer.cpp @@ -25,6 +25,7 @@ #include "source/reduce/remove_block_reduction_opportunity_finder.h" #include "source/reduce/remove_function_reduction_opportunity_finder.h" #include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h" +#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h" #include "source/reduce/remove_selection_reduction_opportunity_finder.h" #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h" #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" @@ -175,6 +176,8 @@ Reducer::ReductionResultStatus Reducer::Run( void Reducer::AddDefaultReductionPasses() { AddReductionPass(spvtools::MakeUnique< RemoveOpNameInstructionReductionOpportunityFinder>()); + AddReductionPass(spvtools::MakeUnique< + RemoveRelaxedPrecisionDecorationOpportunityFinder>()); AddReductionPass( spvtools::MakeUnique()); AddReductionPass( diff --git a/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp new file mode 100644 index 000000000..352cefb68 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h" + +#include "source/reduce/remove_instruction_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context) const { + std::vector> result; + + // Consider all annotation instructions + for (auto& inst : context->module()->annotations()) { + // We are interested in removing instructions of the form: + // SpvOpDecorate %id RelaxedPrecision + // and + // SpvOpMemberDecorate %id member RelaxedPrecision + if ((inst.opcode() == SpvOpDecorate && + inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) || + (inst.opcode() == SpvOpMemberDecorate && + inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) { + result.push_back( + MakeUnique(&inst)); + } + } + return result; +} + +std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const { + return "RemoveRelaxedPrecisionDecorationOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h b/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h new file mode 100644 index 000000000..673049cc8 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h @@ -0,0 +1,36 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to remove relaxed precision decorations. +class RemoveRelaxedPrecisionDecorationOpportunityFinder + : public ReductionOpportunityFinder { + public: + std::vector> GetAvailableOpportunities( + opt::IRContext* context) const override; + + std::string GetName() const override; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_ diff --git a/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp index 8f3243519..dabee50cd 100644 --- a/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp +++ b/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp @@ -21,11 +21,9 @@ namespace spvtools { namespace reduce { -using opt::IRContext; - std::vector> RemoveUnreferencedInstructionReductionOpportunityFinder:: - GetAvailableOpportunities(IRContext* context) const { + GetAvailableOpportunities(opt::IRContext* context) const { std::vector> result; for (auto& function : *context->module()) { diff --git a/3rdparty/spirv-tools/source/val/validate_constants.cpp b/3rdparty/spirv-tools/source/val/validate_constants.cpp index 04544aa3a..565518ba4 100644 --- a/3rdparty/spirv-tools/source/val/validate_constants.cpp +++ b/3rdparty/spirv-tools/source/val/validate_constants.cpp @@ -116,15 +116,13 @@ spv_result_t ValidateConstantComposite(ValidationState_t& _, inst->GetOperandAs(constituent_index); const auto constituent = _.FindDef(constituent_id); if (!constituent || - !(SpvOpConstantComposite == constituent->opcode() || - SpvOpSpecConstantComposite == constituent->opcode() || - SpvOpUndef == constituent->opcode())) { + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { // The message says "... or undef" because the spec does not say // undef is a constant. return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name << " Constituent '" << _.getIdName(constituent_id) - << "' is not a constant composite or undef."; + << "' is not a constant or undef."; } const auto vector = _.FindDef(constituent->type_id()); if (!vector) { diff --git a/3rdparty/spirv-tools/source/val/validate_extensions.cpp b/3rdparty/spirv-tools/source/val/validate_extensions.cpp index ec769db89..1a6460593 100644 --- a/3rdparty/spirv-tools/source/val/validate_extensions.cpp +++ b/3rdparty/spirv-tools/source/val/validate_extensions.cpp @@ -923,7 +923,58 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { case OpenCLLIB::Fract: case OpenCLLIB::Modf: - case OpenCLLIB::Sincos: + case OpenCLLIB::Sincos: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand X to be equal to Result Type"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the last operand to be a pointer"; + } + + if (p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (result_type != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected data type of the pointer to be equal to Result " + "Type"; + } + break; + } + + case OpenCLLIB::Frexp: + case OpenCLLIB::Lgamma_r: case OpenCLLIB::Remquo: { if (!_.IsFloatScalarOrVectorType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -975,57 +1026,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { "CrossWorkgroup, Workgroup or Function"; } - if (result_type != p_data_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected data type of the pointer to be equal to Result " - "Type"; - } - break; - } - - case OpenCLLIB::Frexp: - case OpenCLLIB::Lgamma_r: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } - - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (result_type != x_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected type of operand X to be equal to Result Type"; - } - - const uint32_t p_type = _.GetOperandTypeId(inst, 5); - uint32_t p_storage_class = 0; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected the last operand to be a pointer"; - } - - if (p_storage_class != SpvStorageClassGeneric && - p_storage_class != SpvStorageClassCrossWorkgroup && - p_storage_class != SpvStorageClassWorkgroup && - p_storage_class != SpvStorageClassFunction) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected storage class of the pointer to be Generic, " - "CrossWorkgroup, Workgroup or Function"; - } - if (!_.IsIntScalarOrVectorType(p_data_type) || _.GetBitWidth(p_data_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) diff --git a/3rdparty/spirv-tools/test/link/type_match_test.cpp b/3rdparty/spirv-tools/test/link/type_match_test.cpp index c12ffb300..dae70c163 100644 --- a/3rdparty/spirv-tools/test/link/type_match_test.cpp +++ b/3rdparty/spirv-tools/test/link/type_match_test.cpp @@ -84,44 +84,46 @@ using TypeMatch = spvtest::LinkerTest; MatchF(T##Of##A##Of##B, \ MatchPart1(B, b) MatchPart2(A, a, b) MatchPart2(T, type, a)) +// clang-format off // Basic types -Match1(Int); -Match1(Float); -Match1(Opaque); -Match1(Sampler); -Match1(Event); -Match1(DeviceEvent); -Match1(ReserveId); -Match1(Queue); -Match1(Pipe); -Match1(PipeStorage); -Match1(NamedBarrier); +Match1(Int) +Match1(Float) +Match1(Opaque) +Match1(Sampler) +Match1(Event) +Match1(DeviceEvent) +Match1(ReserveId) +Match1(Queue) +Match1(Pipe) +Match1(PipeStorage) +Match1(NamedBarrier) // Simpler (restricted) compound types -Match2(Vector, Float); -Match3(Matrix, Vector, Float); -Match2(Image, Float); +Match2(Vector, Float) +Match3(Matrix, Vector, Float) +Match2(Image, Float) // Unrestricted compound types #define MatchCompounds1(A) \ - Match2(RuntimeArray, A); \ - Match2(Struct, A); \ - Match2(Pointer, A); \ - Match2(Function, A); \ - Match2(Array, A); + Match2(RuntimeArray, A) \ + Match2(Struct, A) \ + Match2(Pointer, A) \ + Match2(Function, A) \ + Match2(Array, A) #define MatchCompounds2(A, B) \ - Match3(RuntimeArray, A, B); \ - Match3(Struct, A, B); \ - Match3(Pointer, A, B); \ - Match3(Function, A, B); \ - Match3(Array, A, B); + Match3(RuntimeArray, A, B) \ + Match3(Struct, A, B) \ + Match3(Pointer, A, B) \ + Match3(Function, A, B) \ + Match3(Array, A, B) -MatchCompounds1(Float); -MatchCompounds2(Array, Float); -MatchCompounds2(RuntimeArray, Float); -MatchCompounds2(Struct, Float); -MatchCompounds2(Pointer, Float); -MatchCompounds2(Function, Float); +MatchCompounds1(Float) +MatchCompounds2(Array, Float) +MatchCompounds2(RuntimeArray, Float) +MatchCompounds2(Struct, Float) +MatchCompounds2(Pointer, Float) +MatchCompounds2(Function, Float) +// clang-format on // ForwardPointer tests, which don't fit into the previous mold #define MatchFpF(N, CODE) \ @@ -134,11 +136,13 @@ MatchCompounds2(Function, Float); #define MatchFp2(T, A) \ MatchFpF(ForwardPointerOf##T, MatchPart1(A, a) MatchPart2(T, realtype, a)) -MatchFp1(Float); -MatchFp2(Array, Float); -MatchFp2(RuntimeArray, Float); -MatchFp2(Struct, Float); -MatchFp2(Function, Float); + // clang-format off +MatchFp1(Float) +MatchFp2(Array, Float) +MatchFp2(RuntimeArray, Float) +MatchFp2(Struct, Float) +MatchFp2(Function, Float) +// clang-format on } // namespace } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/CMakeLists.txt b/3rdparty/spirv-tools/test/opt/CMakeLists.txt index 366a61f68..7c92f8e82 100644 --- a/3rdparty/spirv-tools/test/opt/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/opt/CMakeLists.txt @@ -52,6 +52,7 @@ add_spvtools_unittest(TARGET opt inline_test.cpp insert_extract_elim_test.cpp inst_bindless_check_test.cpp + inst_buff_addr_check_test.cpp instruction_list_test.cpp instruction_test.cpp ir_builder.cpp @@ -97,6 +98,7 @@ add_spvtools_unittest(TARGET opt value_table_test.cpp vector_dce_test.cpp workaround1209_test.cpp + wrap_opkill_test.cpp LIBS SPIRV-Tools-opt PCH_FILE pch_test_opt ) diff --git a/3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp b/3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp index 04ea0f736..11074c347 100644 --- a/3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp +++ b/3rdparty/spirv-tools/test/opt/desc_sroa_test.cpp @@ -204,6 +204,67 @@ TEST_F(DescriptorScalarReplacementTest, ExpandSSBO) { SinglePassRunAndMatch(text, true); } +TEST_F(DescriptorScalarReplacementTest, NameNewVariables) { + // Checks that if the original variable has a name, then the new variables + // will have a name derived from that name. + const std::string text = R"( +; CHECK: OpName [[var1:%\w+]] "SSBO[0]" +; CHECK: OpName [[var2:%\w+]] "SSBO[1]" +; CHECK: OpDecorate [[var1]] DescriptorSet 0 +; CHECK: OpDecorate [[var1]] Binding 0 +; CHECK: OpDecorate [[var2]] DescriptorSet 0 +; CHECK: OpDecorate [[var2]] Binding 1 +; CHECK: OpTypeStruct +; CHECK: [[struct_type:%\w+]] = OpTypeStruct +; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]] +; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform +; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0 +; CHECK: OpLoad %v4float [[ac1]] +; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0 +; CHECK: OpLoad %v4float [[ac2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %buffers "SSBO" + OpDecorate %buffers DescriptorSet 0 + OpDecorate %buffers Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable + OpDecorate %type_StructuredBuffer_S BufferBlock + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %S = OpTypeStruct %v4float +%_runtimearr_S = OpTypeRuntimeArray %S +%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2 +%_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2 +%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S + %void = OpTypeVoid + %19 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform + %main = OpFunction %void None %19 + %21 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0 + %23 = OpLoad %v4float %22 + %24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1 + %25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0 + %26 = OpLoad %v4float %25 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp b/3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp new file mode 100644 index 000000000..f859ee556 --- /dev/null +++ b/3rdparty/spirv-tools/test/opt/inst_buff_addr_check_test.cpp @@ -0,0 +1,620 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 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. + +// Bindless Check Instrumentation Tests. +// Tests ending with V2 use version 2 record format. + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InstBuffAddrTest = PassTest<::testing::Test>; + +TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // + // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct; + // + // layout(set = 0, binding = 0) uniform ufoo { + // bufStruct data; + // uint offset; + // } u_info; + // + // layout(buffer_reference, std140) buffer bufStruct { + // layout(offset = 0) int a[2]; + // layout(offset = 32) int b; + // }; + // + // void main() { + // u_info.data.b = 0xca7; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddressesEXT +OpExtension "SPV_EXT_physical_storage_buffer" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "data" +OpMemberName %ufoo 1 "offset" +OpName %bufStruct "bufStruct" +OpMemberName %bufStruct 0 "a" +OpMemberName %bufStruct 1 "b" +OpName %u_info "u_info" +OpMemberDecorate %ufoo 0 Offset 0 +OpMemberDecorate %ufoo 1 Offset 8 +OpDecorate %ufoo Block +OpDecorate %_arr_int_uint_2 ArrayStride 16 +OpMemberDecorate %bufStruct 0 Offset 0 +OpMemberDecorate %bufStruct 1 Offset 32 +OpDecorate %bufStruct Block +OpDecorate %u_info DescriptorSet 0 +OpDecorate %u_info Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT +%uint = OpTypeInt 32 0 +%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint +%int = OpTypeInt 32 1 +%uint_2 = OpConstant %uint 2 +%_arr_int_uint_2 = OpTypeArray %int %uint_2 +%bufStruct = OpTypeStruct %_arr_int_uint_2 %int +%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%u_info = OpVariable %_ptr_Uniform_ufoo Uniform +%int_0 = OpConstant %int 0 +%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct +%int_1 = OpConstant %int 1 +%int_3239 = OpConstant %int 3239 +%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "data" +OpMemberName %ufoo 1 "offset" +OpName %bufStruct "bufStruct" +OpMemberName %bufStruct 0 "a" +OpMemberName %bufStruct 1 "b" +OpName %u_info "u_info" +OpMemberDecorate %ufoo 0 Offset 0 +OpMemberDecorate %ufoo 1 Offset 8 +OpDecorate %ufoo Block +OpDecorate %_arr_int_uint_2 ArrayStride 16 +OpMemberDecorate %bufStruct 0 Offset 0 +OpMemberDecorate %bufStruct 1 Offset 32 +OpDecorate %bufStruct Block +OpDecorate %u_info DescriptorSet 0 +OpDecorate %u_info Binding 0 +OpDecorate %_runtimearr_ulong ArrayStride 8 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 2 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_77 Block +OpMemberDecorate %_struct_77 0 Offset 0 +OpMemberDecorate %_struct_77 1 Offset 4 +OpDecorate %79 DescriptorSet 7 +OpDecorate %79 Binding 0 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%void = OpTypeVoid +%8 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_bufStruct PhysicalStorageBufferEXT +%uint = OpTypeInt 32 0 +%ufoo = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_bufStruct %uint +%int = OpTypeInt 32 1 +%uint_2 = OpConstant %uint 2 +%_arr_int_uint_2 = OpTypeArray %int %uint_2 +%bufStruct = OpTypeStruct %_arr_int_uint_2 %int +%_ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer PhysicalStorageBufferEXT %bufStruct +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%u_info = OpVariable %_ptr_Uniform_ufoo Uniform +%int_0 = OpConstant %int 0 +%_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBufferEXT_bufStruct +%int_1 = OpConstant %int 1 +%int_3239 = OpConstant %int 3239 +%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int +%ulong = OpTypeInt 64 0 +%uint_4 = OpConstant %uint 4 +%bool = OpTypeBool +%28 = OpTypeFunction %bool %ulong %uint +%uint_1 = OpConstant %uint 1 +%_runtimearr_ulong = OpTypeRuntimeArray %ulong +%_struct_39 = OpTypeStruct %_runtimearr_ulong +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong +%uint_0 = OpConstant %uint 0 +%uint_32 = OpConstant %uint 32 +%70 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_77 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77 +%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_23 = OpConstant %uint 23 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_48 = OpConstant %uint 48 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0 +%18 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %17 +%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %18 %int_1 +OpStore %22 %int_3239 Aligned 16 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %8 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBufferEXT_bufStruct %u_info %int_0 +%21 = OpLoad %_ptr_PhysicalStorageBufferEXT_bufStruct %20 +%22 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %21 %int_1 +%24 = OpConvertPtrToU %ulong %22 +%61 = OpFunctionCall %bool %26 %24 %uint_4 +OpSelectionMerge %62 None +OpBranchConditional %61 %63 %64 +%63 = OpLabel +OpStore %22 %int_3239 Aligned 16 +OpBranch %62 +%64 = OpLabel +%65 = OpUConvert %uint %24 +%67 = OpShiftRightLogical %ulong %24 %uint_32 +%68 = OpUConvert %uint %67 +%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68 +OpBranch %62 +%62 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%26 = OpFunction %bool None %28 +%29 = OpFunctionParameter %ulong +%30 = OpFunctionParameter %uint +%31 = OpLabel +OpBranch %32 +%32 = OpLabel +%34 = OpPhi %uint %uint_1 %31 %35 %33 +OpLoopMerge %37 %33 None +OpBranch %33 +%33 = OpLabel +%35 = OpIAdd %uint %34 %uint_1 +%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35 +%45 = OpLoad %ulong %44 +%46 = OpUGreaterThan %bool %45 %29 +OpBranchConditional %46 %37 %32 +%37 = OpLabel +%47 = OpISub %uint %35 %uint_1 +%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47 +%49 = OpLoad %ulong %48 +%50 = OpISub %ulong %29 %49 +%51 = OpUConvert %ulong %30 +%52 = OpIAdd %ulong %50 %51 +%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0 +%54 = OpLoad %ulong %53 +%55 = OpUConvert %uint %54 +%56 = OpISub %uint %47 %uint_1 +%57 = OpIAdd %uint %56 %55 +%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57 +%59 = OpLoad %ulong %58 +%60 = OpULessThanEqual %bool %52 %59 +OpReturnValue %60 +OpFunctionEnd +%69 = OpFunction %void None %70 +%71 = OpFunctionParameter %uint +%72 = OpFunctionParameter %uint +%73 = OpFunctionParameter %uint +%74 = OpFunctionParameter %uint +%75 = OpLabel +%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0 +%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10 +%84 = OpIAdd %uint %83 %uint_10 +%85 = OpArrayLength %uint %79 1 +%86 = OpULessThanEqual %bool %84 %85 +OpSelectionMerge %87 None +OpBranchConditional %86 %88 %87 +%88 = OpLabel +%89 = OpIAdd %uint %83 %uint_0 +%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89 +OpStore %90 %uint_10 +%92 = OpIAdd %uint %83 %uint_1 +%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92 +OpStore %93 %uint_23 +%94 = OpIAdd %uint %83 %uint_2 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94 +OpStore %95 %71 +%98 = OpIAdd %uint %83 %uint_3 +%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98 +OpStore %99 %uint_5 +%103 = OpLoad %v3uint %gl_GlobalInvocationID +%104 = OpCompositeExtract %uint %103 0 +%105 = OpCompositeExtract %uint %103 1 +%106 = OpCompositeExtract %uint %103 2 +%107 = OpIAdd %uint %83 %uint_4 +%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107 +OpStore %108 %104 +%109 = OpIAdd %uint %83 %uint_5 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109 +OpStore %110 %105 +%112 = OpIAdd %uint %83 %uint_6 +%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112 +OpStore %113 %106 +%115 = OpIAdd %uint %83 %uint_7 +%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115 +OpStore %116 %72 +%118 = OpIAdd %uint %83 %uint_8 +%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118 +OpStore %119 %73 +%121 = OpIAdd %uint %83 %uint_9 +%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121 +OpStore %122 %74 +OpBranch %87 +%87 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, 2u); +} + +TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + + // // forward reference + // layout(buffer_reference) buffer blockType; + + // layout(buffer_reference, std430, buffer_reference_align = 16) buffer + // blockType { + // int x; + // blockType next; + // }; + + // layout(std430) buffer rootBlock { + // blockType root; + // } r; + + // void main() + // { + // blockType b = r.root; + // b = b.next; + // b.x = 531; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddressesEXT +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %blockType "blockType" +OpMemberName %blockType 0 "x" +OpMemberName %blockType 1 "next" +OpName %rootBlock "rootBlock" +OpMemberName %rootBlock 0 "root" +OpName %r "r" +OpMemberDecorate %blockType 0 Offset 0 +OpMemberDecorate %blockType 1 Offset 8 +OpDecorate %blockType Block +OpMemberDecorate %rootBlock 0 Offset 0 +OpDecorate %rootBlock Block +OpDecorate %r DescriptorSet 0 +OpDecorate %r Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT +%int = OpTypeInt 32 1 +%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType +%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType +%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType +%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock +%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType +%int_1 = OpConstant %int 1 +%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType +%int_531 = OpConstant %int 531 +%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Int64 +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %blockType "blockType" +OpMemberName %blockType 0 "x" +OpMemberName %blockType 1 "next" +OpName %rootBlock "rootBlock" +OpMemberName %rootBlock 0 "root" +OpName %r "r" +OpMemberDecorate %blockType 0 Offset 0 +OpMemberDecorate %blockType 1 Offset 8 +OpDecorate %blockType Block +OpMemberDecorate %rootBlock 0 Offset 0 +OpDecorate %rootBlock Block +OpDecorate %r DescriptorSet 0 +OpDecorate %r Binding 0 +OpDecorate %_runtimearr_ulong ArrayStride 8 +OpDecorate %_struct_45 Block +OpMemberDecorate %_struct_45 0 Offset 0 +OpDecorate %47 DescriptorSet 7 +OpDecorate %47 Binding 2 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_84 Block +OpMemberDecorate %_struct_84 0 Offset 0 +OpMemberDecorate %_struct_84 1 Offset 4 +OpDecorate %86 DescriptorSet 7 +OpDecorate %86 Binding 0 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBufferEXT_blockType PhysicalStorageBufferEXT +%int = OpTypeInt 32 1 +%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBufferEXT_blockType +%_ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %blockType +%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBufferEXT_blockType +%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock +%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBufferEXT_blockType +%int_1 = OpConstant %int 1 +%_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType = OpTypePointer PhysicalStorageBufferEXT %_ptr_PhysicalStorageBufferEXT_blockType +%int_531 = OpConstant %int 531 +%_ptr_PhysicalStorageBufferEXT_int = OpTypePointer PhysicalStorageBufferEXT %int +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%ulong = OpTypeInt 64 0 +%uint_8 = OpConstant %uint 8 +%bool = OpTypeBool +%34 = OpTypeFunction %bool %ulong %uint +%uint_1 = OpConstant %uint 1 +%_runtimearr_ulong = OpTypeRuntimeArray %ulong +%_struct_45 = OpTypeStruct %_runtimearr_ulong +%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45 +%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer +%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong +%uint_0 = OpConstant %uint 0 +%uint_32 = OpConstant %uint 32 +%77 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_84 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84 +%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_9 = OpConstant %uint 9 +%uint_44 = OpConstant %uint 44 +%132 = OpConstantNull %_ptr_PhysicalStorageBufferEXT_blockType +%uint_46 = OpConstant %uint 46 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0 +%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16 +%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1 +%22 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8 +%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %22 %int_0 +OpStore %26 %int_531 Aligned 16 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBufferEXT_blockType %r %int_0 +%17 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %16 +%21 = OpAccessChain %_ptr_PhysicalStorageBufferEXT__ptr_PhysicalStorageBufferEXT_blockType %17 %int_1 +%30 = OpConvertPtrToU %ulong %21 +%67 = OpFunctionCall %bool %32 %30 %uint_8 +OpSelectionMerge %68 None +OpBranchConditional %67 %69 %70 +%69 = OpLabel +%71 = OpLoad %_ptr_PhysicalStorageBufferEXT_blockType %21 Aligned 8 +OpBranch %68 +%70 = OpLabel +%72 = OpUConvert %uint %30 +%74 = OpShiftRightLogical %ulong %30 %uint_32 +%75 = OpUConvert %uint %74 +%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75 +OpBranch %68 +%68 = OpLabel +%133 = OpPhi %_ptr_PhysicalStorageBufferEXT_blockType %71 %69 %132 %70 +%26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_int %133 %int_0 +%134 = OpConvertPtrToU %ulong %26 +%135 = OpFunctionCall %bool %32 %134 %uint_4 +OpSelectionMerge %136 None +OpBranchConditional %135 %137 %138 +%137 = OpLabel +OpStore %26 %int_531 Aligned 16 +OpBranch %136 +%138 = OpLabel +%139 = OpUConvert %uint %134 +%140 = OpShiftRightLogical %ulong %134 %uint_32 +%141 = OpUConvert %uint %140 +%143 = OpFunctionCall %void %76 %uint_46 %uint_2 %139 %141 +OpBranch %136 +%136 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%32 = OpFunction %bool None %34 +%35 = OpFunctionParameter %ulong +%36 = OpFunctionParameter %uint +%37 = OpLabel +OpBranch %38 +%38 = OpLabel +%40 = OpPhi %uint %uint_1 %37 %41 %39 +OpLoopMerge %43 %39 None +OpBranch %39 +%39 = OpLabel +%41 = OpIAdd %uint %40 %uint_1 +%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41 +%51 = OpLoad %ulong %50 +%52 = OpUGreaterThan %bool %51 %35 +OpBranchConditional %52 %43 %38 +%43 = OpLabel +%53 = OpISub %uint %41 %uint_1 +%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53 +%55 = OpLoad %ulong %54 +%56 = OpISub %ulong %35 %55 +%57 = OpUConvert %ulong %36 +%58 = OpIAdd %ulong %56 %57 +%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0 +%60 = OpLoad %ulong %59 +%61 = OpUConvert %uint %60 +%62 = OpISub %uint %53 %uint_1 +%63 = OpIAdd %uint %62 %61 +%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63 +%65 = OpLoad %ulong %64 +%66 = OpULessThanEqual %bool %58 %65 +OpReturnValue %66 +OpFunctionEnd +%76 = OpFunction %void None %77 +%78 = OpFunctionParameter %uint +%79 = OpFunctionParameter %uint +%80 = OpFunctionParameter %uint +%81 = OpFunctionParameter %uint +%82 = OpLabel +%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0 +%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10 +%92 = OpIAdd %uint %91 %uint_10 +%93 = OpArrayLength %uint %86 1 +%94 = OpULessThanEqual %bool %92 %93 +OpSelectionMerge %95 None +OpBranchConditional %94 %96 %95 +%96 = OpLabel +%97 = OpIAdd %uint %91 %uint_0 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97 +OpStore %98 %uint_10 +%100 = OpIAdd %uint %91 %uint_1 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100 +OpStore %101 %uint_23 +%102 = OpIAdd %uint %91 %uint_2 +%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102 +OpStore %103 %78 +%106 = OpIAdd %uint %91 %uint_3 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106 +OpStore %107 %uint_5 +%111 = OpLoad %v3uint %gl_GlobalInvocationID +%112 = OpCompositeExtract %uint %111 0 +%113 = OpCompositeExtract %uint %111 1 +%114 = OpCompositeExtract %uint %111 2 +%115 = OpIAdd %uint %91 %uint_4 +%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115 +OpStore %116 %112 +%117 = OpIAdd %uint %91 %uint_5 +%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117 +OpStore %118 %113 +%120 = OpIAdd %uint %91 %uint_6 +%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120 +OpStore %121 %114 +%123 = OpIAdd %uint %91 %uint_7 +%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123 +OpStore %124 %79 +%125 = OpIAdd %uint %91 %uint_8 +%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125 +OpStore %126 %80 +%128 = OpIAdd %uint %91 %uint_9 +%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128 +OpStore %129 %81 +OpBranch %95 +%95 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, 2u); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/pass_fixture.h b/3rdparty/spirv-tools/test/opt/pass_fixture.h index a4d749f78..53fb206fa 100644 --- a/3rdparty/spirv-tools/test/opt/pass_fixture.h +++ b/3rdparty/spirv-tools/test/opt/pass_fixture.h @@ -75,7 +75,9 @@ class PassTest : public TestT { const auto status = pass->Run(context()); std::vector binary; - context()->module()->ToBinary(&binary, skip_nop); + if (status != Pass::Status::Failure) { + context()->module()->ToBinary(&binary, skip_nop); + } return std::make_tuple(binary, status); } @@ -241,15 +243,18 @@ class PassTest : public TestT { context()->set_preserve_spec_constants( OptimizerOptions()->preserve_spec_constants_); - manager_->Run(context()); + auto status = manager_->Run(context()); + EXPECT_NE(status, Pass::Status::Failure); - std::vector binary; - context()->module()->ToBinary(&binary, /* skip_nop = */ false); + if (status != Pass::Status::Failure) { + std::vector binary; + context()->module()->ToBinary(&binary, /* skip_nop = */ false); - std::string optimized; - SpirvTools tools(env_); - EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_)); - EXPECT_EQ(expected, optimized); + std::string optimized; + SpirvTools tools(env_); + EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_)); + EXPECT_EQ(expected, optimized); + } } void SetAssembleOptions(uint32_t assemble_options) { diff --git a/3rdparty/spirv-tools/test/opt/private_to_local_test.cpp b/3rdparty/spirv-tools/test/opt/private_to_local_test.cpp index d15484056..12306529a 100644 --- a/3rdparty/spirv-tools/test/opt/private_to_local_test.cpp +++ b/3rdparty/spirv-tools/test/opt/private_to_local_test.cpp @@ -419,6 +419,39 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(PrivateToLocalTest, IdBoundOverflow1) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginLowerLeft + OpSource HLSL 84 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeStruct %7 + %4194302 = OpTypeStruct %8 %8 + %9 = OpTypeStruct %8 %8 + %11 = OpTypePointer Private %7 + %18 = OpTypeStruct %6 %9 + %12 = OpVariable %11 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpLoad %7 %12 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/scalar_replacement_test.cpp b/3rdparty/spirv-tools/test/opt/scalar_replacement_test.cpp index 00f8e17b6..3cf46ca13 100644 --- a/3rdparty/spirv-tools/test/opt/scalar_replacement_test.cpp +++ b/3rdparty/spirv-tools/test/opt/scalar_replacement_test.cpp @@ -1621,7 +1621,7 @@ TEST_F(ScalarReplacementTest, TestAccessChainWithNoIndexes) { } // Test that id overflow is handled gracefully. -TEST_F(ScalarReplacementTest, IdBoundOverflow) { +TEST_F(ScalarReplacementTest, IdBoundOverflow1) { const std::string text = R"( OpCapability ImageQuery OpMemoryModel Logical GLSL450 @@ -1652,8 +1652,103 @@ OpFunctionEnd {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; SetMessageConsumer(GetTestMessageConsumer(messages)); - auto result = - SinglePassRunAndDisassemble(text, true, false); + auto result = SinglePassRunToBinary(text, true, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +// Test that id overflow is handled gracefully. +TEST_F(ScalarReplacementTest, IdBoundOverflow2) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %17 +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeStruct %7 +%9 = OpTypePointer Function %8 +%16 = OpTypePointer Output %7 +%21 = OpTypeInt 32 1 +%22 = OpConstant %21 0 +%23 = OpTypePointer Function %7 +%17 = OpVariable %16 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%4194300 = OpVariable %23 Function +%10 = OpVariable %9 Function +%4194301 = OpAccessChain %23 %10 %22 +%4194302 = OpLoad %7 %4194301 +OpStore %4194300 %4194302 +%15 = OpLoad %7 %4194300 +OpStore %17 %15 +OpReturn +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +// Test that id overflow is handled gracefully. +TEST_F(ScalarReplacementTest, IdBoundOverflow3) { + const std::string text = R"( +OpCapability InterpolationFunction +OpExtension "z" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeStruct %6 %6 +%9 = OpTypePointer Function %7 +%18 = OpTypeFunction %7 %9 +%21 = OpTypeInt 32 0 +%22 = OpConstant %21 4293000676 +%4194302 = OpConstantNull %6 +%4 = OpFunction %2 Inline|Pure %3 +%786464 = OpLabel +%4194298 = OpVariable %9 Function +%10 = OpVariable %9 Function +%4194299 = OpUDiv %21 %22 %22 +%4194300 = OpLoad %7 %10 +%50959 = OpLoad %7 %4194298 +OpKill +OpFunctionEnd +%1 = OpFunction %7 None %18 +%19 = OpFunctionParameter %9 +%147667 = OpLabel +%2044391 = OpUDiv %21 %22 %22 +%25 = OpLoad %7 %19 +OpReturnValue %25 +OpFunctionEnd +%4194295 = OpFunction %2 None %3 +%4194296 = OpLabel +OpKill +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true, false); EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); } diff --git a/3rdparty/spirv-tools/test/opt/types_test.cpp b/3rdparty/spirv-tools/test/opt/types_test.cpp index fd9880680..82e40405c 100644 --- a/3rdparty/spirv-tools/test/opt/types_test.cpp +++ b/3rdparty/spirv-tools/test/opt/types_test.cpp @@ -65,17 +65,18 @@ class SameTypeTest : public ::testing::Test { #define TestMultipleInstancesOfTheSameType(ty, ...) \ TestMultipleInstancesOfTheSameTypeQualified(ty, Simple, __VA_ARGS__) -TestMultipleInstancesOfTheSameType(Void); -TestMultipleInstancesOfTheSameType(Bool); -TestMultipleInstancesOfTheSameType(Integer, 32, true); -TestMultipleInstancesOfTheSameType(Float, 64); -TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3); -TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4); +// clang-format off +TestMultipleInstancesOfTheSameType(Void) +TestMultipleInstancesOfTheSameType(Bool) +TestMultipleInstancesOfTheSameType(Integer, 32, true) +TestMultipleInstancesOfTheSameType(Float, 64) +TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3) +TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4) TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), SpvDimCube, 0, 0, 1, 1, SpvImageFormatRgb10A2, - SpvAccessQualifierWriteOnly); -TestMultipleInstancesOfTheSameType(Sampler); -TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get()); + SpvAccessQualifierWriteOnly) +TestMultipleInstancesOfTheSameType(Sampler) +TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get()) // There are three classes of arrays, based on the kinds of length information // they have. // 1. Array length is a constant or spec constant without spec ID, with literals @@ -85,34 +86,35 @@ TestMultipleInstancesOfTheSameTypeQualified(Array, LenConstant, u32_t_.get(), { 0, 9999, - }}); + }}) // 2. Array length is a spec constant with a given spec id. TestMultipleInstancesOfTheSameTypeQualified(Array, LenSpecId, u32_t_.get(), - Array::LengthInfo{42, {1, 99}}); + Array::LengthInfo{42, {1, 99}}) // 3. Array length is an OpSpecConstantOp expression TestMultipleInstancesOfTheSameTypeQualified(Array, LenDefiningId, u32_t_.get(), - Array::LengthInfo{42, {2, 42}}); + Array::LengthInfo{42, {2, 42}}) -TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get()); +TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get()) TestMultipleInstancesOfTheSameType(Struct, std::vector{ - u32_t_.get(), f64_t_.get()}); -TestMultipleInstancesOfTheSameType(Opaque, "testing rocks"); -TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput); + u32_t_.get(), f64_t_.get()}) +TestMultipleInstancesOfTheSameType(Opaque, "testing rocks") +TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput) TestMultipleInstancesOfTheSameType(Function, u32_t_.get(), - {f64_t_.get(), f64_t_.get()}); -TestMultipleInstancesOfTheSameType(Event); -TestMultipleInstancesOfTheSameType(DeviceEvent); -TestMultipleInstancesOfTheSameType(ReserveId); -TestMultipleInstancesOfTheSameType(Queue); -TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite); -TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform); -TestMultipleInstancesOfTheSameType(PipeStorage); -TestMultipleInstancesOfTheSameType(NamedBarrier); -TestMultipleInstancesOfTheSameType(AccelerationStructureNV); + {f64_t_.get(), f64_t_.get()}) +TestMultipleInstancesOfTheSameType(Event) +TestMultipleInstancesOfTheSameType(DeviceEvent) +TestMultipleInstancesOfTheSameType(ReserveId) +TestMultipleInstancesOfTheSameType(Queue) +TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite) +TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform) +TestMultipleInstancesOfTheSameType(PipeStorage) +TestMultipleInstancesOfTheSameType(NamedBarrier) +TestMultipleInstancesOfTheSameType(AccelerationStructureNV) #undef TestMultipleInstanceOfTheSameType #undef TestMultipleInstanceOfTheSameTypeQual std::vector> GenerateAllTypes() { + // clang-format on // Types in this test case are only equal to themselves, nothing else. std::vector> types; diff --git a/3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp b/3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp new file mode 100644 index 000000000..df1b865eb --- /dev/null +++ b/3rdparty/spirv-tools/test/opt/wrap_opkill_test.cpp @@ -0,0 +1,267 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "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 WrapOpKillTest = PassTest<::testing::Test>; + +TEST_F(WrapOpKillTest, SingleOpKill) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleOpKillInSameFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpUnreachable +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %true %17 %18 + %17 = OpLabel + OpKill + %18 = OpLabel + OpKill + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleOpKillInDifferentFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]] +; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]] +; CHECK: [[orig_kill1]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[orig_kill2]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpUnreachable +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpBranchConditional %true %12 %9 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + %13 = OpFunctionCall %void %14 + %15 = OpFunctionCall %void %16 + OpBranch %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %void None %4 + %17 = OpLabel + OpKill + OpFunctionEnd + %16 = OpFunction %void None %4 + %18 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow1) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 Pure|Const %3 +%4194302 = OpLabel +OpKill +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow2) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 Pure|Const %3 +%4194301 = OpLabel +OpKill +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow3) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 Pure|Const %3 +%4194300 = OpLabel +OpKill +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt index 964abdd5e..2d3b37834 100644 --- a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt @@ -24,6 +24,7 @@ add_spvtools_unittest(TARGET reduce remove_block_test.cpp remove_function_test.cpp remove_opname_instruction_test.cpp + remove_relaxed_precision_decoration_test.cpp remove_selection_test.cpp remove_unreferenced_instruction_test.cpp structured_loop_to_selection_test.cpp diff --git a/3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp b/3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp new file mode 100644 index 000000000..f9ff081cc --- /dev/null +++ b/3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/reduction_pass.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) { + const std::string source = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, source, kReduceAssembleOption); + const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) { + const std::string source = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f" + OpName %12 "i" + OpName %16 "v" + OpName %19 "S" + OpMemberName %19 0 "a" + OpMemberName %19 1 "b" + OpMemberName %19 2 "c" + OpName %21 "s" + OpDecorate %8 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpMemberDecorate %19 0 RelaxedPrecision + OpMemberDecorate %19 1 RelaxedPrecision + OpMemberDecorate %19 2 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 22 + %14 = OpTypeVector %6 2 + %15 = OpTypePointer Function %14 + %19 = OpTypeStruct %10 %6 %14 + %20 = OpTypePointer Function %19 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + %21 = OpVariable %20 Function + OpStore %8 %9 + OpStore %12 %13 + %17 = OpLoad %6 %8 + %18 = OpCompositeConstruct %14 %17 %17 + OpStore %16 %18 + %22 = OpLoad %10 %12 + %23 = OpLoad %6 %8 + %24 = OpLoad %14 %16 + %25 = OpCompositeConstruct %19 %22 %23 %24 + OpStore %21 %25 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, source, kReduceAssembleOption); + const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(11, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + } + + const std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f" + OpName %12 "i" + OpName %16 "v" + OpName %19 "S" + OpMemberName %19 0 "a" + OpMemberName %19 1 "b" + OpMemberName %19 2 "c" + OpName %21 "s" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 22 + %14 = OpTypeVector %6 2 + %15 = OpTypePointer Function %14 + %19 = OpTypeStruct %10 %6 %14 + %20 = OpTypePointer Function %19 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + %21 = OpVariable %20 Function + OpStore %8 %9 + OpStore %12 %13 + %17 = OpLoad %6 %8 + %18 = OpCompositeConstruct %14 %17 %17 + OpStore %16 %18 + %22 = OpLoad %10 %12 + %23 = OpLoad %6 %8 + %24 = OpLoad %14 %16 + %25 = OpCompositeConstruct %19 %22 %23 %24 + OpStore %21 %25 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, expected, context.get()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/tools/opt/flags.py b/3rdparty/spirv-tools/test/tools/opt/flags.py index a89477cc3..49e2cabc5 100644 --- a/3rdparty/spirv-tools/test/tools/opt/flags.py +++ b/3rdparty/spirv-tools/test/tools/opt/flags.py @@ -57,7 +57,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4, """Tests that spirv-opt accepts all valid optimization flags.""" flags = [ - '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids', + '--wrap-opkill', '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids', '--convert-local-access-chains', '--copy-propagate-arrays', '--eliminate-dead-branches', '--eliminate-dead-code-aggressive', '--eliminate-dead-const', @@ -76,6 +76,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_4, '--unify-const' ] expected_passes = [ + 'wrap-opkill', 'ccp', 'cfg-cleanup', 'combine-access-chains', @@ -134,6 +135,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4, flags = ['-O'] expected_passes = [ + 'wrap-opkill', 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive', @@ -181,6 +183,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_4, flags = ['-Os'] expected_passes = [ + 'wrap-opkill', 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive', @@ -221,6 +224,7 @@ class TestLegalizationPasses(expect.ValidObjectFile1_4, flags = ['--legalize-hlsl'] expected_passes = [ + 'wrap-opkill', 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive', diff --git a/3rdparty/spirv-tools/test/val/val_constants_test.cpp b/3rdparty/spirv-tools/test/val/val_constants_test.cpp index 72ce8dfdb..2499f5ca4 100644 --- a/3rdparty/spirv-tools/test/val/val_constants_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_constants_test.cpp @@ -442,6 +442,22 @@ OpMemoryModel Logical GLSL450 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } +TEST_F(ValidateConstant, NullMatrix) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%null_vector = OpConstantNull %v2float +%null_matrix = OpConstantComposite %mat2x2 %null_vector %null_vector +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/val_ext_inst_test.cpp b/3rdparty/spirv-tools/test/val/val_ext_inst_test.cpp index 73cb48f74..67df43d13 100644 --- a/3rdparty/spirv-tools/test/val/val_ext_inst_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_ext_inst_test.cpp @@ -5315,10 +5315,10 @@ INSTANTIATE_TEST_SUITE_P(AllFractLike, ValidateOpenCLStdFractLike, TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) { const std::string body = R"( -%var_f32 = OpVariable %f32_ptr_function Function -%var_f32vec2 = OpVariable %f32vec2_ptr_function Function -%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32 -%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_f32vec2 +%var_u32 = OpVariable %u32_ptr_function Function +%var_u32vec2 = OpVariable %u32vec2_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32 +%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_u32vec2 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -5327,8 +5327,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) { TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) { const std::string body = R"( -%var_f32 = OpVariable %f32_ptr_function Function -%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_f32 +%var_u32 = OpVariable %u32_ptr_function Function +%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_u32 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -5341,8 +5341,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) { TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) { const std::string body = R"( -%var_f32 = OpVariable %f32_ptr_function Function -%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_f32 +%var_u32 = OpVariable %f32_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_u32 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -5355,8 +5355,8 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) { TEST_F(ValidateExtInst, OpenCLStdRemquoYWrongType) { const std::string body = R"( -%var_f32 = OpVariable %f32_ptr_function Function -%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_f32 +%var_u32 = OpVariable %f32_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_u32 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -5395,17 +5395,44 @@ TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongStorageClass) { TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataType) { const std::string body = R"( -%var_u32 = OpVariable %u32_ptr_function Function -%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32 +%var_f32 = OpVariable %f32_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected data type of the pointer to be a 32-bit int " + "scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataTypeWidth) { + const std::string body = R"( +%var_u64 = OpVariable %u64_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u64 +)"; + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected data type of the pointer to be a 32-bit int " + "scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongNumberOfComponents) { + const std::string body = R"( +%var_u32vec2 = OpVariable %u32vec2_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32vec2 )"; CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "OpenCL.std remquo: " - "expected data type of the pointer to be equal to Result Type")); + HasSubstr("OpenCL.std remquo: " + "expected data type of the pointer to have the same number " + "of components as Result Type")); } TEST_P(ValidateOpenCLStdFrexpLike, Success) { diff --git a/3rdparty/spirv-tools/test/val/val_id_test.cpp b/3rdparty/spirv-tools/test/val/val_id_test.cpp index 299e38eb1..ec5715c1f 100644 --- a/3rdparty/spirv-tools/test/val/val_id_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_id_test.cpp @@ -1715,7 +1715,7 @@ TEST_F(ValidateIdWithMessage, EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpSpecConstantComposite Constituent '7[%7]' is " - "not a constant composite or undef.")); + "not a constant or undef.")); } // Invalid: Composite contains a column that is *not* a vector (it's an array)