| 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_INSTRUMENT_PASS_H_ |
| 18 | #define LIBSPIRV_OPT_INSTRUMENT_PASS_H_ |
| 19 | |
| 20 | #include <list> |
| 21 | #include <memory> |
| 22 | #include <vector> |
| 23 | |
| 24 | #include "source/opt/ir_builder.h" |
| 25 | #include "source/opt/pass.h" |
| 26 | #include "spirv-tools/instrument.hpp" |
| 27 | |
| 28 | // This is a base class to assist in the creation of passes which instrument |
| 29 | // shader modules. More specifically, passes which replace instructions with a |
| 30 | // larger and more capable set of instructions. Commonly, these new |
| 31 | // instructions will add testing of operands and execute different |
| 32 | // instructions depending on the outcome, including outputting of debug |
| 33 | // information into a buffer created especially for that purpose. |
| 34 | // |
| 35 | // This class contains helper functions to create an InstProcessFunction, |
| 36 | // which is the heart of any derived class implementing a specific |
| 37 | // instrumentation pass. It takes an instruction as an argument, decides |
| 38 | // if it should be instrumented, and generates code to replace it. This class |
| 39 | // also supplies function InstProcessEntryPointCallTree which applies the |
| 40 | // InstProcessFunction to every reachable instruction in a module and replaces |
| 41 | // the instruction with new instructions if generated. |
| 42 | // |
| 43 | // Chief among the helper functions are output code generation functions, |
| 44 | // used to generate code in the shader which writes data to output buffers |
| 45 | // associated with that validation. Currently one such function, |
| 46 | // GenDebugStreamWrite, exists. Other such functions may be added in the |
| 47 | // future. Each is accompanied by documentation describing the format of |
| 48 | // its output buffer. |
| 49 | // |
| 50 | // A validation pass may read or write multiple buffers. All such buffers |
| 51 | // are located in a single debug descriptor set whose index is passed at the |
| 52 | // creation of the instrumentation pass. The bindings of the buffers used by |
| 53 | // a validation pass are permanantly assigned and fixed and documented by |
| 54 | // the kDebugOutput* static consts. |
| 55 | |
| 56 | namespace spvtools { |
| 57 | namespace opt { |
| 58 | |
| 59 | // Validation Ids |
| 60 | // These are used to identify the general validation being done and map to |
| 61 | // its output buffers. |
| 62 | static const uint32_t kInstValidationIdBindless = 0; |
| 63 | static const uint32_t kInstValidationIdBuffAddr = 1; |
| 64 | static const uint32_t kInstValidationIdDebugPrintf = 2; |
| 65 | |
| 66 | class InstrumentPass : public Pass { |
| 67 | using cbb_ptr = const BasicBlock*; |
| 68 | |
| 69 | public: |
| 70 | using InstProcessFunction = |
| 71 | std::function<void(BasicBlock::iterator, UptrVectorIterator<BasicBlock>, |
| 72 | uint32_t, std::vector<std::unique_ptr<BasicBlock>>*)>; |
| 73 | |
| 74 | ~InstrumentPass() override = default; |
| 75 | |
| 76 | IRContext::Analysis GetPreservedAnalyses() override { |
| 77 | return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations | |
| 78 | IRContext::kAnalysisCombinators | IRContext::kAnalysisNameMap | |
| 79 | IRContext::kAnalysisBuiltinVarId | IRContext::kAnalysisConstants; |
| 80 | } |
| 81 | |
| 82 | protected: |
| 83 | // Create instrumentation pass for |validation_id| which utilizes descriptor |
| 84 | // set |desc_set| for debug input and output buffers and writes |shader_id| |
| 85 | // into debug output records. |
| 86 | InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id) |
| 87 | : Pass(), |
| 88 | desc_set_(desc_set), |
| 89 | shader_id_(shader_id), |
| 90 | validation_id_(validation_id), |
| 91 | version_(2u) {} |
| 92 | // Create instrumentation pass for |validation_id| which utilizes descriptor |
| 93 | // set |desc_set| for debug input and output buffers and writes |shader_id| |
| 94 | // into debug output records with format |version|. Deprecated. |
| 95 | InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id, |
| 96 | uint32_t version) |
| 97 | : Pass(), |
| 98 | desc_set_(desc_set), |
| 99 | shader_id_(shader_id), |
| 100 | validation_id_(validation_id), |
| 101 | version_(version) {} |
| 102 | |
| 103 | // Initialize state for instrumentation of module. |
| 104 | void InitializeInstrument(); |
| 105 | |
| 106 | // Call |pfn| on all instructions in all functions in the call tree of the |
| 107 | // entry points in |module|. If code is generated for an instruction, replace |
| 108 | // the instruction's block with the new blocks that are generated. Continue |
| 109 | // processing at the top of the last new block. |
| 110 | bool InstProcessEntryPointCallTree(InstProcessFunction& pfn); |
| 111 | |
| 112 | // Move all code in |ref_block_itr| preceding the instruction |ref_inst_itr| |
| 113 | // to be instrumented into block |new_blk_ptr|. |
| 114 | void MovePreludeCode(BasicBlock::iterator ref_inst_itr, |
| 115 | UptrVectorIterator<BasicBlock> ref_block_itr, |
| 116 | std::unique_ptr<BasicBlock>* new_blk_ptr); |
| 117 | |
| 118 | // Move all code in |ref_block_itr| succeeding the instruction |ref_inst_itr| |
| 119 | // to be instrumented into block |new_blk_ptr|. |
| 120 | void MovePostludeCode(UptrVectorIterator<BasicBlock> ref_block_itr, |
| 121 | BasicBlock* new_blk_ptr); |
| 122 | |
| 123 | // Generate instructions in |builder| which will atomically fetch and |
| 124 | // increment the size of the debug output buffer stream of the current |
| 125 | // validation and write a record to the end of the stream, if enough space |
| 126 | // in the buffer remains. The record will contain the index of the function |
| 127 | // and instruction within that function |func_idx, instruction_idx| which |
| 128 | // generated the record. It will also contain additional information to |
| 129 | // identify the instance of the shader, depending on the stage |stage_idx| |
| 130 | // of the shader. Finally, the record will contain validation-specific |
| 131 | // data contained in |validation_ids| which will identify the validation |
| 132 | // error as well as the values involved in the error. |
| 133 | // |
| 134 | // The output buffer binding written to by the code generated by the function |
| 135 | // is determined by the validation id specified when each specific |
| 136 | // instrumentation pass is created. |
| 137 | // |
| 138 | // The output buffer is a sequence of 32-bit values with the following |
| 139 | // format (where all elements are unsigned 32-bit unless otherwise noted): |
| 140 | // |
| 141 | // Size |
| 142 | // Record0 |
| 143 | // Record1 |
| 144 | // Record2 |
| 145 | // ... |
| 146 | // |
| 147 | // Size is the number of 32-bit values that have been written or |
| 148 | // attempted to be written to the output buffer, excluding the Size. It is |
| 149 | // initialized to 0. If the size of attempts to write the buffer exceeds |
| 150 | // the actual size of the buffer, it is possible that this field can exceed |
| 151 | // the actual size of the buffer. |
| 152 | // |
| 153 | // Each Record* is a variable-length sequence of 32-bit values with the |
| 154 | // following format defined using static const offsets in the .cpp file: |
| 155 | // |
| 156 | // Record Size |
| 157 | // Shader ID |
| 158 | // Instruction Index |
| 159 | // Stage |
| 160 | // Stage-specific Word 0 |
| 161 | // Stage-specific Word 1 |
| 162 | // ... |
| 163 | // Validation Error Code |
| 164 | // Validation-specific Word 0 |
| 165 | // Validation-specific Word 1 |
| 166 | // Validation-specific Word 2 |
| 167 | // ... |
| 168 | // |
| 169 | // Each record consists of three subsections: members common across all |
| 170 | // validation, members specific to the stage, and members specific to a |
| 171 | // validation. |
| 172 | // |
| 173 | // The Record Size is the number of 32-bit words in the record, including |
| 174 | // the Record Size word. |
| 175 | // |
| 176 | // Shader ID is a value that identifies which shader has generated the |
| 177 | // validation error. It is passed when the instrumentation pass is created. |
| 178 | // |
| 179 | // The Instruction Index is the position of the instruction within the |
| 180 | // SPIR-V file which is in error. |
| 181 | // |
| 182 | // The Stage is the pipeline stage which has generated the error as defined |
| 183 | // by the SpvExecutionModel_ enumeration. This is used to interpret the |
| 184 | // following Stage-specific words. |
| 185 | // |
| 186 | // The Stage-specific Words identify which invocation of the shader generated |
| 187 | // the error. Every stage will write a fixed number of words. Vertex shaders |
| 188 | // will write the Vertex and Instance ID. Fragment shaders will write |
| 189 | // FragCoord.xy. Compute shaders will write the GlobalInvocation ID. |
| 190 | // The tesselation eval shader will write the Primitive ID and TessCoords.uv. |
| 191 | // The tesselation control shader and geometry shader will write the |
| 192 | // Primitive ID and Invocation ID. |
| 193 | // |
| 194 | // The Validation Error Code specifies the exact error which has occurred. |
| 195 | // These are enumerated with the kInstError* static consts. This allows |
| 196 | // multiple validation layers to use the same, single output buffer. |
| 197 | // |
| 198 | // The Validation-specific Words are a validation-specific number of 32-bit |
| 199 | // words which give further information on the validation error that |
| 200 | // occurred. These are documented further in each file containing the |
| 201 | // validation-specific class which derives from this base class. |
| 202 | // |
| 203 | // Because the code that is generated checks against the size of the buffer |
| 204 | // before writing, the size of the debug out buffer can be used by the |
| 205 | // validation layer to control the number of error records that are written. |
| 206 | void GenDebugStreamWrite(uint32_t instruction_idx, uint32_t stage_idx, |
| 207 | const std::vector<uint32_t>& validation_ids, |
| 208 | InstructionBuilder* builder); |
| 209 | |
| 210 | // Generate in |builder| instructions to read the unsigned integer from the |
| 211 | // input buffer specified by the offsets in |offset_ids|. Given offsets |
| 212 | // o0, o1, ... oN, and input buffer ibuf, return the id for the value: |
| 213 | // |
| 214 | // ibuf[...ibuf[ibuf[o0]+o1]...+oN] |
| 215 | // |
| 216 | // The binding and the format of the input buffer is determined by each |
| 217 | // specific validation, which is specified at the creation of the pass. |
| 218 | uint32_t GenDebugDirectRead(const std::vector<uint32_t>& offset_ids, |
| 219 | InstructionBuilder* builder); |
| 220 | |
| 221 | // Generate code to cast |value_id| to unsigned, if needed. Return |
| 222 | // an id to the unsigned equivalent. |
| 223 | uint32_t GenUintCastCode(uint32_t value_id, InstructionBuilder* builder); |
| 224 | |
| 225 | // Return new label. |
| 226 | std::unique_ptr<Instruction> NewLabel(uint32_t label_id); |
| 227 | |
| 228 | // Return id for 32-bit unsigned type |
| 229 | uint32_t GetUintId(); |
| 230 | |
| 231 | // Return id for 64-bit unsigned type |
| 232 | uint32_t GetUint64Id(); |
| 233 | |
| 234 | // Return id for 8-bit unsigned type |
| 235 | uint32_t GetUint8Id(); |
| 236 | |
| 237 | // Return id for 32-bit unsigned type |
| 238 | uint32_t GetBoolId(); |
| 239 | |
| 240 | // Return id for void type |
| 241 | uint32_t GetVoidId(); |
| 242 | |
| 243 | // Return pointer to type for runtime array of uint |
| 244 | analysis::Type* GetUintXRuntimeArrayType(uint32_t width, |
| 245 | analysis::Type** rarr_ty); |
| 246 | |
| 247 | // Return pointer to type for runtime array of uint |
| 248 | analysis::Type* GetUintRuntimeArrayType(uint32_t width); |
| 249 | |
| 250 | // Return id for buffer uint type |
| 251 | uint32_t GetOutputBufferPtrId(); |
| 252 | |
| 253 | // Return id for buffer uint type |
| 254 | uint32_t GetInputBufferTypeId(); |
| 255 | |
| 256 | // Return id for buffer uint type |
| 257 | uint32_t GetInputBufferPtrId(); |
| 258 | |
| 259 | // Return binding for output buffer for current validation. |
| 260 | uint32_t GetOutputBufferBinding(); |
| 261 | |
| 262 | // Return binding for input buffer for current validation. |
| 263 | uint32_t GetInputBufferBinding(); |
| 264 | |
| 265 | // Add storage buffer extension if needed |
| 266 | void AddStorageBufferExt(); |
| 267 | |
| 268 | // Return id for debug output buffer |
| 269 | uint32_t GetOutputBufferId(); |
| 270 | |
| 271 | // Return id for debug input buffer |
| 272 | uint32_t GetInputBufferId(); |
| 273 | |
| 274 | // Return id for 32-bit float type |
| 275 | uint32_t GetFloatId(); |
| 276 | |
| 277 | // Return id for v4float type |
| 278 | uint32_t GetVec4FloatId(); |
| 279 | |
| 280 | // Return id for uint vector type of |length| |
| 281 | uint32_t GetVecUintId(uint32_t length); |
| 282 | |
| 283 | // Return id for v4uint type |
| 284 | uint32_t GetVec4UintId(); |
| 285 | |
| 286 | // Return id for v3uint type |
| 287 | uint32_t GetVec3UintId(); |
| 288 | |
| 289 | // Return id for output function. Define if it doesn't exist with |
| 290 | // |val_spec_param_cnt| validation-specific uint32 parameters. |
| 291 | uint32_t GetStreamWriteFunctionId(uint32_t stage_idx, |
| 292 | uint32_t val_spec_param_cnt); |
| 293 | |
| 294 | // Return id for input function taking |param_cnt| uint32 parameters. Define |
| 295 | // if it doesn't exist. |
| 296 | uint32_t GetDirectReadFunctionId(uint32_t param_cnt); |
| 297 | |
| 298 | // Apply instrumentation function |pfn| to every instruction in |func|. |
| 299 | // If code is generated for an instruction, replace the instruction's |
| 300 | // block with the new blocks that are generated. Continue processing at the |
| 301 | // top of the last new block. |
| 302 | bool InstrumentFunction(Function* func, uint32_t stage_idx, |
| 303 | InstProcessFunction& pfn); |
| 304 | |
| 305 | // Call |pfn| on all functions in the call tree of the function |
| 306 | // ids in |roots|. |
| 307 | bool InstProcessCallTreeFromRoots(InstProcessFunction& pfn, |
| 308 | std::queue<uint32_t>* roots, |
| 309 | uint32_t stage_idx); |
| 310 | |
| 311 | // Gen code into |builder| to write |field_value_id| into debug output |
| 312 | // buffer at |base_offset_id| + |field_offset|. |
| 313 | void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset, |
| 314 | uint32_t field_value_id, |
| 315 | InstructionBuilder* builder); |
| 316 | |
| 317 | // Generate instructions into |builder| which will write the members |
| 318 | // of the debug output record common for all stages and validations at |
| 319 | // |base_off|. |
| 320 | void GenCommonStreamWriteCode(uint32_t record_sz, uint32_t instruction_idx, |
| 321 | uint32_t stage_idx, uint32_t base_off, |
| 322 | InstructionBuilder* builder); |
| 323 | |
| 324 | // Generate instructions into |builder| which will write |
| 325 | // |uint_frag_coord_id| at |component| of the record at |base_offset_id| of |
| 326 | // the debug output buffer . |
| 327 | void GenFragCoordEltDebugOutputCode(uint32_t base_offset_id, |
| 328 | uint32_t uint_frag_coord_id, |
| 329 | uint32_t component, |
| 330 | InstructionBuilder* builder); |
| 331 | |
| 332 | // Generate instructions into |builder| which will load |var_id| and return |
| 333 | // its result id. |
| 334 | uint32_t GenVarLoad(uint32_t var_id, InstructionBuilder* builder); |
| 335 | |
| 336 | // Generate instructions into |builder| which will load the uint |builtin_id| |
| 337 | // and write it into the debug output buffer at |base_off| + |builtin_off|. |
| 338 | void GenBuiltinOutputCode(uint32_t builtin_id, uint32_t builtin_off, |
| 339 | uint32_t base_off, InstructionBuilder* builder); |
| 340 | |
| 341 | // Generate instructions into |builder| which will write the |stage_idx|- |
| 342 | // specific members of the debug output stream at |base_off|. |
| 343 | void GenStageStreamWriteCode(uint32_t stage_idx, uint32_t base_off, |
| 344 | InstructionBuilder* builder); |
| 345 | |
| 346 | // Return true if instruction must be in the same block that its result |
| 347 | // is used. |
| 348 | bool IsSameBlockOp(const Instruction* inst) const; |
| 349 | |
| 350 | // Clone operands which must be in same block as consumer instructions. |
| 351 | // Look in same_blk_pre for instructions that need cloning. Look in |
| 352 | // same_blk_post for instructions already cloned. Add cloned instruction |
| 353 | // to same_blk_post. |
| 354 | void CloneSameBlockOps( |
| 355 | std::unique_ptr<Instruction>* inst, |
| 356 | std::unordered_map<uint32_t, uint32_t>* same_blk_post, |
| 357 | std::unordered_map<uint32_t, Instruction*>* same_blk_pre, |
| 358 | BasicBlock* block_ptr); |
| 359 | |
| 360 | // Update phis in succeeding blocks to point to new last block |
| 361 | void UpdateSucceedingPhis( |
| 362 | std::vector<std::unique_ptr<BasicBlock>>& new_blocks); |
| 363 | |
| 364 | // Debug descriptor set index |
| 365 | uint32_t desc_set_; |
| 366 | |
| 367 | // Shader module ID written into output record |
| 368 | uint32_t shader_id_; |
| 369 | |
| 370 | // Map from function id to function pointer. |
| 371 | std::unordered_map<uint32_t, Function*> id2function_; |
| 372 | |
| 373 | // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt |
| 374 | // CFG. It has functionality not present in CFG. Consolidate. |
| 375 | std::unordered_map<uint32_t, BasicBlock*> id2block_; |
| 376 | |
| 377 | // Map from instruction's unique id to offset in original file. |
| 378 | std::unordered_map<uint32_t, uint32_t> uid2offset_; |
| 379 | |
| 380 | // result id for OpConstantFalse |
| 381 | uint32_t validation_id_; |
| 382 | |
| 383 | // id for output buffer variable |
| 384 | uint32_t output_buffer_id_; |
| 385 | |
| 386 | // ptr type id for output buffer element |
| 387 | uint32_t output_buffer_ptr_id_; |
| 388 | |
| 389 | // ptr type id for input buffer element |
| 390 | uint32_t input_buffer_ptr_id_; |
| 391 | |
| 392 | // id for debug output function |
| 393 | std::unordered_map<uint32_t, uint32_t> param2output_func_id_; |
| 394 | |
| 395 | // ids for debug input functions |
| 396 | std::unordered_map<uint32_t, uint32_t> param2input_func_id_; |
| 397 | |
| 398 | // id for input buffer variable |
| 399 | uint32_t input_buffer_id_; |
| 400 | |
| 401 | // id for 32-bit float type |
| 402 | uint32_t float_id_; |
| 403 | |
| 404 | // id for v4float type |
| 405 | uint32_t v4float_id_; |
| 406 | |
| 407 | // id for v4uint type |
| 408 | uint32_t v4uint_id_; |
| 409 | |
| 410 | // id for v3uint type |
| 411 | uint32_t v3uint_id_; |
| 412 | |
| 413 | // id for 32-bit unsigned type |
| 414 | uint32_t uint_id_; |
| 415 | |
| 416 | // id for 64-bit unsigned type |
| 417 | uint32_t uint64_id_; |
| 418 | |
| 419 | // id for 8-bit unsigned type |
| 420 | uint32_t uint8_id_; |
| 421 | |
| 422 | // id for bool type |
| 423 | uint32_t bool_id_; |
| 424 | |
| 425 | // id for void type |
| 426 | uint32_t void_id_; |
| 427 | |
| 428 | // Record format version |
| 429 | uint32_t version_; |
| 430 | |
| 431 | // boolean to remember storage buffer extension |
| 432 | bool storage_buffer_ext_defined_; |
| 433 | |
| 434 | // runtime array of uint type |
| 435 | analysis::Type* uint64_rarr_ty_; |
| 436 | |
| 437 | // runtime array of uint type |
| 438 | analysis::Type* uint32_rarr_ty_; |
| 439 | |
| 440 | // Pre-instrumentation same-block insts |
| 441 | std::unordered_map<uint32_t, Instruction*> same_block_pre_; |
| 442 | |
| 443 | // Post-instrumentation same-block op ids |
| 444 | std::unordered_map<uint32_t, uint32_t> same_block_post_; |
| 445 | }; |
| 446 | |
| 447 | } // namespace opt |
| 448 | } // namespace spvtools |
| 449 | |
| 450 | #endif // LIBSPIRV_OPT_INSTRUMENT_PASS_H_ |
| 451 | |