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 | |