1 // Copyright (c) 2018 The Khronos Group Inc. 2 // Copyright (c) 2018 Valve Corporation 3 // Copyright (c) 2018 LunarG Inc. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #ifndef LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_ 18 #define LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_ 19 20 #include "instrument_pass.h" 21 22 namespace spvtools { 23 namespace opt { 24 25 // This class/pass is designed to support the bindless (descriptor indexing) 26 // GPU-assisted validation layer of 27 // https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and 28 // external design may change as the layer evolves. 29 class InstBindlessCheckPass : public InstrumentPass { 30 public: InstBindlessCheckPass(uint32_t desc_set,uint32_t shader_id,bool desc_idx_enable,bool desc_init_enable,bool buffer_bounds_enable,bool texel_buffer_enable,bool opt_direct_reads)31 InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id, 32 bool desc_idx_enable, bool desc_init_enable, 33 bool buffer_bounds_enable, bool texel_buffer_enable, 34 bool opt_direct_reads) 35 : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, 36 opt_direct_reads), 37 desc_idx_enabled_(desc_idx_enable), 38 desc_init_enabled_(desc_init_enable), 39 buffer_bounds_enabled_(buffer_bounds_enable), 40 texel_buffer_enabled_(texel_buffer_enable) {} 41 42 ~InstBindlessCheckPass() override = default; 43 44 // See optimizer.hpp for pass user documentation. 45 Status Process() override; 46 name()47 const char* name() const override { return "inst-bindless-check-pass"; } 48 49 private: 50 // These functions do bindless checking instrumentation on a single 51 // instruction which references through a descriptor (ie references into an 52 // image or buffer). Refer to Vulkan API for further information on 53 // descriptors. GenDescIdxCheckCode checks that an index into a descriptor 54 // array (array of images or buffers) is in-bounds. GenDescInitCheckCode 55 // checks that the referenced descriptor has been initialized, if the 56 // SPV_EXT_descriptor_indexing extension is enabled, and initialized large 57 // enough to handle the reference, if RobustBufferAccess is disabled. 58 // GenDescInitCheckCode checks for uniform and storage buffer overrun. 59 // GenTexBuffCheckCode checks for texel buffer overrun and should be 60 // run after GenDescInitCheckCode to first make sure that the descriptor 61 // is initialized because it uses OpImageQuerySize on the descriptor. 62 // 63 // The functions are designed to be passed to 64 // InstrumentPass::InstProcessEntryPointCallTree(), which applies the 65 // function to each instruction in a module and replaces the instruction 66 // if warranted. 67 // 68 // If |ref_inst_itr| is a bindless reference, return in |new_blocks| the 69 // result of instrumenting it with validation code within its block at 70 // |ref_block_itr|. The validation code first executes a check for the 71 // specific condition called for. If the check passes, it executes 72 // the remainder of the reference, otherwise writes a record to the debug 73 // output buffer stream including |function_idx, instruction_idx, stage_idx| 74 // and replaces the reference with the null value of the original type. The 75 // block at |ref_block_itr| can just be replaced with the blocks in 76 // |new_blocks|, which will contain at least two blocks. The last block will 77 // comprise all instructions following |ref_inst_itr|, 78 // preceded by a phi instruction. 79 // 80 // These instrumentation functions utilize GenDebugDirectRead() to read data 81 // from the debug input buffer, specifically the lengths of variable length 82 // descriptor arrays, and the initialization status of each descriptor. 83 // The format of the debug input buffer is documented in instrument.hpp. 84 // 85 // These instrumentation functions utilize GenDebugStreamWrite() to write its 86 // error records. The validation-specific part of the error record will 87 // have the format: 88 // 89 // Validation Error Code (=kInstErrorBindlessBounds) 90 // Descriptor Index 91 // Descriptor Array Size 92 // 93 // The Descriptor Index is the index which has been determined to be 94 // out-of-bounds. 95 // 96 // The Descriptor Array Size is the size of the descriptor array which was 97 // indexed. 98 void GenDescIdxCheckCode( 99 BasicBlock::iterator ref_inst_itr, 100 UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, 101 std::vector<std::unique_ptr<BasicBlock>>* new_blocks); 102 103 void GenDescInitCheckCode( 104 BasicBlock::iterator ref_inst_itr, 105 UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, 106 std::vector<std::unique_ptr<BasicBlock>>* new_blocks); 107 108 void GenTexBuffCheckCode( 109 BasicBlock::iterator ref_inst_itr, 110 UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, 111 std::vector<std::unique_ptr<BasicBlock>>* new_blocks); 112 113 // Generate instructions into |builder| to read length of runtime descriptor 114 // array |var_id| from debug input buffer and return id of value. 115 uint32_t GenDebugReadLength(uint32_t var_id, InstructionBuilder* builder); 116 117 // Generate instructions into |builder| to read initialization status of 118 // descriptor array |image_id| at |index_id| from debug input buffer and 119 // return id of value. 120 uint32_t GenDebugReadInit(uint32_t image_id, uint32_t index_id, 121 InstructionBuilder* builder); 122 123 // Analysis data for descriptor reference components, generated by 124 // AnalyzeDescriptorReference. It is necessary and sufficient for further 125 // analysis and regeneration of the reference. 126 typedef struct RefAnalysis { 127 uint32_t desc_load_id; 128 uint32_t image_id; 129 uint32_t load_id; 130 uint32_t ptr_id; 131 uint32_t var_id; 132 uint32_t desc_idx_id; 133 uint32_t strg_class; 134 Instruction* ref_inst; 135 } RefAnalysis; 136 137 // Return size of type |ty_id| in bytes. Use |matrix_stride| and |col_major| 138 // for matrix type, or for vector type if vector is |in_matrix|. 139 uint32_t ByteSize(uint32_t ty_id, uint32_t matrix_stride, bool col_major, 140 bool in_matrix); 141 142 // Return stride of type |ty_id| with decoration |stride_deco|. Return 0 143 // if not found 144 uint32_t FindStride(uint32_t ty_id, uint32_t stride_deco); 145 146 // Generate index of last byte referenced by buffer reference |ref| 147 uint32_t GenLastByteIdx(RefAnalysis* ref, InstructionBuilder* builder); 148 149 // Clone original image computation starting at |image_id| into |builder|. 150 // This may generate more than one instruction if necessary. 151 uint32_t CloneOriginalImage(uint32_t image_id, InstructionBuilder* builder); 152 153 // Clone original original reference encapsulated by |ref| into |builder|. 154 // This may generate more than one instruction if necessary. 155 uint32_t CloneOriginalReference(RefAnalysis* ref, 156 InstructionBuilder* builder); 157 158 // If |inst| references through an image, return the id of the image it 159 // references through. Else return 0. 160 uint32_t GetImageId(Instruction* inst); 161 162 // Get pointee type inst of pointer value |ptr_inst|. 163 Instruction* GetPointeeTypeInst(Instruction* ptr_inst); 164 165 // Analyze descriptor reference |ref_inst| and save components into |ref|. 166 // Return true if |ref_inst| is a descriptor reference, false otherwise. 167 bool AnalyzeDescriptorReference(Instruction* ref_inst, RefAnalysis* ref); 168 169 // Generate instrumentation code for generic test result |check_id|, starting 170 // with |builder| of block |new_blk_ptr|, adding new blocks to |new_blocks|. 171 // Generate conditional branch to a valid or invalid branch. Generate valid 172 // block which does original reference |ref|. Generate invalid block which 173 // writes debug error output utilizing |ref|, |error_id|, |length_id| and 174 // |stage_idx|. Generate merge block for valid and invalid branches. Kill 175 // original reference. 176 void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t offset_id, 177 uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref, 178 std::vector<std::unique_ptr<BasicBlock>>* new_blocks); 179 180 // Initialize state for instrumenting bindless checking 181 void InitializeInstBindlessCheck(); 182 183 // Apply GenDescIdxCheckCode to every instruction in module. Then apply 184 // GenDescInitCheckCode to every instruction in module. 185 Pass::Status ProcessImpl(); 186 187 // Enable instrumentation of runtime array length checking 188 bool desc_idx_enabled_; 189 190 // Enable instrumentation of descriptor initialization checking 191 bool desc_init_enabled_; 192 193 // Enable instrumentation of uniform and storage buffer overrun checking 194 bool buffer_bounds_enabled_; 195 196 // Enable instrumentation of texel buffer overrun checking 197 bool texel_buffer_enabled_; 198 199 // Mapping from variable to descriptor set 200 std::unordered_map<uint32_t, uint32_t> var2desc_set_; 201 202 // Mapping from variable to binding 203 std::unordered_map<uint32_t, uint32_t> var2binding_; 204 }; 205 206 } // namespace opt 207 } // namespace spvtools 208 209 #endif // LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_ 210