Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 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 | #include "instrument_pass.h" |
| 18 | |
| 19 | #include "source/cfa.h" |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 20 | #include "source/spirv_constant.h" |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 21 | |
| 22 | namespace { |
| 23 | |
| 24 | // Common Parameter Positions |
| 25 | static const int kInstCommonParamInstIdx = 0; |
| 26 | static const int kInstCommonParamCnt = 1; |
| 27 | |
| 28 | // Indices of operands in SPIR-V instructions |
| 29 | static const int kEntryPointExecutionModelInIdx = 0; |
| 30 | static const int kEntryPointFunctionIdInIdx = 1; |
| 31 | |
| 32 | } // anonymous namespace |
| 33 | |
| 34 | namespace spvtools { |
| 35 | namespace opt { |
| 36 | |
| 37 | void InstrumentPass::MovePreludeCode( |
| 38 | BasicBlock::iterator ref_inst_itr, |
| 39 | UptrVectorIterator<BasicBlock> ref_block_itr, |
| 40 | std::unique_ptr<BasicBlock>* new_blk_ptr) { |
| 41 | same_block_pre_.clear(); |
| 42 | same_block_post_.clear(); |
| 43 | // Initialize new block. Reuse label from original block. |
| 44 | new_blk_ptr->reset(new BasicBlock(std::move(ref_block_itr->GetLabel()))); |
| 45 | // Move contents of original ref block up to ref instruction. |
| 46 | for (auto cii = ref_block_itr->begin(); cii != ref_inst_itr; |
| 47 | cii = ref_block_itr->begin()) { |
| 48 | Instruction* inst = &*cii; |
| 49 | inst->RemoveFromList(); |
| 50 | std::unique_ptr<Instruction> mv_ptr(inst); |
| 51 | // Remember same-block ops for possible regeneration. |
| 52 | if (IsSameBlockOp(&*mv_ptr)) { |
| 53 | auto* sb_inst_ptr = mv_ptr.get(); |
| 54 | same_block_pre_[mv_ptr->result_id()] = sb_inst_ptr; |
| 55 | } |
| 56 | (*new_blk_ptr)->AddInstruction(std::move(mv_ptr)); |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | void InstrumentPass::MovePostludeCode( |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 61 | UptrVectorIterator<BasicBlock> ref_block_itr, BasicBlock* new_blk_ptr) { |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 62 | // new_blk_ptr->reset(new BasicBlock(NewLabel(ref_block_itr->id()))); |
| 63 | // Move contents of original ref block. |
| 64 | for (auto cii = ref_block_itr->begin(); cii != ref_block_itr->end(); |
| 65 | cii = ref_block_itr->begin()) { |
| 66 | Instruction* inst = &*cii; |
| 67 | inst->RemoveFromList(); |
| 68 | std::unique_ptr<Instruction> mv_inst(inst); |
| 69 | // Regenerate any same-block instruction that has not been seen in the |
| 70 | // current block. |
| 71 | if (same_block_pre_.size() > 0) { |
| 72 | CloneSameBlockOps(&mv_inst, &same_block_post_, &same_block_pre_, |
| 73 | new_blk_ptr); |
| 74 | // Remember same-block ops in this block. |
| 75 | if (IsSameBlockOp(&*mv_inst)) { |
| 76 | const uint32_t rid = mv_inst->result_id(); |
| 77 | same_block_post_[rid] = rid; |
| 78 | } |
| 79 | } |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 80 | new_blk_ptr->AddInstruction(std::move(mv_inst)); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 81 | } |
| 82 | } |
| 83 | |
| 84 | std::unique_ptr<Instruction> InstrumentPass::NewLabel(uint32_t label_id) { |
| 85 | std::unique_ptr<Instruction> newLabel( |
| 86 | new Instruction(context(), SpvOpLabel, 0, label_id, {})); |
| 87 | get_def_use_mgr()->AnalyzeInstDefUse(&*newLabel); |
| 88 | return newLabel; |
| 89 | } |
| 90 | |
| 91 | uint32_t InstrumentPass::GenUintCastCode(uint32_t val_id, |
| 92 | InstructionBuilder* builder) { |
| 93 | // Cast value to 32-bit unsigned if necessary |
| 94 | if (get_def_use_mgr()->GetDef(val_id)->type_id() == GetUintId()) |
| 95 | return val_id; |
| 96 | return builder->AddUnaryOp(GetUintId(), SpvOpBitcast, val_id)->result_id(); |
| 97 | } |
| 98 | |
| 99 | void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id, |
| 100 | uint32_t field_offset, |
| 101 | uint32_t field_value_id, |
| 102 | InstructionBuilder* builder) { |
| 103 | // Cast value to 32-bit unsigned if necessary |
| 104 | uint32_t val_id = GenUintCastCode(field_value_id, builder); |
| 105 | // Store value |
| 106 | Instruction* data_idx_inst = |
| 107 | builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id, |
| 108 | builder->GetUintConstantId(field_offset)); |
| 109 | uint32_t buf_id = GetOutputBufferId(); |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 110 | uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 111 | Instruction* achain_inst = |
| 112 | builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, |
| 113 | builder->GetUintConstantId(kDebugOutputDataOffset), |
| 114 | data_idx_inst->result_id()); |
| 115 | (void)builder->AddBinaryOp(0, SpvOpStore, achain_inst->result_id(), val_id); |
| 116 | } |
| 117 | |
| 118 | void InstrumentPass::GenCommonStreamWriteCode(uint32_t record_sz, |
| 119 | uint32_t inst_id, |
| 120 | uint32_t stage_idx, |
| 121 | uint32_t base_offset_id, |
| 122 | InstructionBuilder* builder) { |
| 123 | // Store record size |
| 124 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutSize, |
| 125 | builder->GetUintConstantId(record_sz), builder); |
| 126 | // Store Shader Id |
| 127 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutShaderId, |
| 128 | builder->GetUintConstantId(shader_id_), builder); |
| 129 | // Store Instruction Idx |
| 130 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutInstructionIdx, inst_id, |
| 131 | builder); |
| 132 | // Store Stage Idx |
| 133 | GenDebugOutputFieldCode(base_offset_id, kInstCommonOutStageIdx, |
| 134 | builder->GetUintConstantId(stage_idx), builder); |
| 135 | } |
| 136 | |
| 137 | void InstrumentPass::GenFragCoordEltDebugOutputCode( |
| 138 | uint32_t base_offset_id, uint32_t uint_frag_coord_id, uint32_t element, |
| 139 | InstructionBuilder* builder) { |
| 140 | Instruction* element_val_inst = builder->AddIdLiteralOp( |
| 141 | GetUintId(), SpvOpCompositeExtract, uint_frag_coord_id, element); |
| 142 | GenDebugOutputFieldCode(base_offset_id, kInstFragOutFragCoordX + element, |
| 143 | element_val_inst->result_id(), builder); |
| 144 | } |
| 145 | |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 146 | uint32_t InstrumentPass::GenVarLoad(uint32_t var_id, |
| 147 | InstructionBuilder* builder) { |
| 148 | Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); |
| 149 | uint32_t type_id = GetPointeeTypeId(var_inst); |
| 150 | Instruction* load_inst = builder->AddUnaryOp(type_id, SpvOpLoad, var_id); |
| 151 | return load_inst->result_id(); |
| 152 | } |
| 153 | |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 154 | void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id, |
| 155 | uint32_t builtin_off, |
| 156 | uint32_t base_offset_id, |
| 157 | InstructionBuilder* builder) { |
| 158 | // Load and store builtin |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 159 | uint32_t load_id = GenVarLoad(builtin_id, builder); |
| 160 | GenDebugOutputFieldCode(base_offset_id, builtin_off, load_id, builder); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx, |
| 164 | uint32_t base_offset_id, |
| 165 | InstructionBuilder* builder) { |
| 166 | // TODO(greg-lunarg): Add support for all stages |
| 167 | switch (stage_idx) { |
| 168 | case SpvExecutionModelVertex: { |
| 169 | // Load and store VertexId and InstanceId |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 170 | GenBuiltinOutputCode( |
| 171 | context()->GetBuiltinInputVarId(SpvBuiltInVertexIndex), |
| 172 | kInstVertOutVertexIndex, base_offset_id, builder); |
| 173 | GenBuiltinOutputCode( |
| 174 | context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex), |
| 175 | kInstVertOutInstanceIndex, base_offset_id, builder); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 176 | } break; |
| 177 | case SpvExecutionModelGLCompute: { |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 178 | // Load and store GlobalInvocationId. |
| 179 | uint32_t load_id = GenVarLoad( |
| 180 | context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId), |
| 181 | builder); |
| 182 | Instruction* x_inst = builder->AddIdLiteralOp( |
| 183 | GetUintId(), SpvOpCompositeExtract, load_id, 0); |
| 184 | Instruction* y_inst = builder->AddIdLiteralOp( |
| 185 | GetUintId(), SpvOpCompositeExtract, load_id, 1); |
| 186 | Instruction* z_inst = builder->AddIdLiteralOp( |
| 187 | GetUintId(), SpvOpCompositeExtract, load_id, 2); |
| 188 | if (version_ == 1) { |
| 189 | // For version 1 format, as a stopgap, pack uvec3 into first word: |
| 190 | // x << 21 | y << 10 | z. Second word is unused. (DEPRECATED) |
| 191 | Instruction* x_shft_inst = builder->AddBinaryOp( |
| 192 | GetUintId(), SpvOpShiftLeftLogical, x_inst->result_id(), |
| 193 | builder->GetUintConstantId(21)); |
| 194 | Instruction* y_shft_inst = builder->AddBinaryOp( |
| 195 | GetUintId(), SpvOpShiftLeftLogical, y_inst->result_id(), |
| 196 | builder->GetUintConstantId(10)); |
| 197 | Instruction* x_or_y_inst = builder->AddBinaryOp( |
| 198 | GetUintId(), SpvOpBitwiseOr, x_shft_inst->result_id(), |
| 199 | y_shft_inst->result_id()); |
| 200 | Instruction* x_or_y_or_z_inst = |
| 201 | builder->AddBinaryOp(GetUintId(), SpvOpBitwiseOr, |
| 202 | x_or_y_inst->result_id(), z_inst->result_id()); |
| 203 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationId, |
| 204 | x_or_y_or_z_inst->result_id(), builder); |
| 205 | } else { |
| 206 | // For version 2 format, write all three words |
| 207 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX, |
| 208 | x_inst->result_id(), builder); |
| 209 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY, |
| 210 | y_inst->result_id(), builder); |
| 211 | GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ, |
| 212 | z_inst->result_id(), builder); |
| 213 | } |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 214 | } break; |
| 215 | case SpvExecutionModelGeometry: { |
| 216 | // Load and store PrimitiveId and InvocationId. |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 217 | GenBuiltinOutputCode( |
| 218 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), |
| 219 | kInstGeomOutPrimitiveId, base_offset_id, builder); |
| 220 | GenBuiltinOutputCode( |
| 221 | context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), |
| 222 | kInstGeomOutInvocationId, base_offset_id, builder); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 223 | } break; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 224 | case SpvExecutionModelTessellationControl: { |
| 225 | // Load and store InvocationId and PrimitiveId |
| 226 | GenBuiltinOutputCode( |
| 227 | context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), |
| 228 | kInstTessCtlOutInvocationId, base_offset_id, builder); |
| 229 | GenBuiltinOutputCode( |
| 230 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), |
| 231 | kInstTessCtlOutPrimitiveId, base_offset_id, builder); |
| 232 | } break; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 233 | case SpvExecutionModelTessellationEvaluation: { |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 234 | if (version_ == 1) { |
| 235 | // For format version 1, load and store InvocationId. |
| 236 | GenBuiltinOutputCode( |
| 237 | context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), |
| 238 | kInstTessOutInvocationId, base_offset_id, builder); |
| 239 | } else { |
| 240 | // For format version 2, load and store PrimitiveId and TessCoord.uv |
| 241 | GenBuiltinOutputCode( |
| 242 | context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), |
| 243 | kInstTessEvalOutPrimitiveId, base_offset_id, builder); |
| 244 | uint32_t load_id = GenVarLoad( |
| 245 | context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder); |
| 246 | Instruction* u_inst = builder->AddIdLiteralOp( |
| 247 | GetUintId(), SpvOpCompositeExtract, load_id, 0); |
| 248 | Instruction* v_inst = builder->AddIdLiteralOp( |
| 249 | GetUintId(), SpvOpCompositeExtract, load_id, 1); |
| 250 | GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU, |
| 251 | u_inst->result_id(), builder); |
| 252 | GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV, |
| 253 | v_inst->result_id(), builder); |
| 254 | } |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 255 | } break; |
| 256 | case SpvExecutionModelFragment: { |
| 257 | // Load FragCoord and convert to Uint |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 258 | Instruction* frag_coord_inst = builder->AddUnaryOp( |
| 259 | GetVec4FloatId(), SpvOpLoad, |
| 260 | context()->GetBuiltinInputVarId(SpvBuiltInFragCoord)); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 261 | Instruction* uint_frag_coord_inst = builder->AddUnaryOp( |
| 262 | GetVec4UintId(), SpvOpBitcast, frag_coord_inst->result_id()); |
| 263 | for (uint32_t u = 0; u < 2u; ++u) |
| 264 | GenFragCoordEltDebugOutputCode( |
| 265 | base_offset_id, uint_frag_coord_inst->result_id(), u, builder); |
| 266 | } break; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 267 | case SpvExecutionModelRayGenerationNV: |
| 268 | case SpvExecutionModelIntersectionNV: |
| 269 | case SpvExecutionModelAnyHitNV: |
| 270 | case SpvExecutionModelClosestHitNV: |
| 271 | case SpvExecutionModelMissNV: |
| 272 | case SpvExecutionModelCallableNV: { |
| 273 | // Load and store LaunchIdNV. |
| 274 | uint32_t launch_id = GenVarLoad( |
| 275 | context()->GetBuiltinInputVarId(SpvBuiltInLaunchIdNV), builder); |
| 276 | Instruction* x_launch_inst = builder->AddIdLiteralOp( |
| 277 | GetUintId(), SpvOpCompositeExtract, launch_id, 0); |
| 278 | Instruction* y_launch_inst = builder->AddIdLiteralOp( |
| 279 | GetUintId(), SpvOpCompositeExtract, launch_id, 1); |
| 280 | Instruction* z_launch_inst = builder->AddIdLiteralOp( |
| 281 | GetUintId(), SpvOpCompositeExtract, launch_id, 2); |
| 282 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdX, |
| 283 | x_launch_inst->result_id(), builder); |
| 284 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdY, |
| 285 | y_launch_inst->result_id(), builder); |
| 286 | GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdZ, |
| 287 | z_launch_inst->result_id(), builder); |
| 288 | } break; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 289 | default: { assert(false && "unsupported stage"); } break; |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | void InstrumentPass::GenDebugStreamWrite( |
| 294 | uint32_t instruction_idx, uint32_t stage_idx, |
| 295 | const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) { |
| 296 | // Call debug output function. Pass func_idx, instruction_idx and |
| 297 | // validation ids as args. |
| 298 | uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size()); |
| 299 | uint32_t output_func_id = GetStreamWriteFunctionId(stage_idx, val_id_cnt); |
| 300 | std::vector<uint32_t> args = {output_func_id, |
| 301 | builder->GetUintConstantId(instruction_idx)}; |
| 302 | (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end()); |
| 303 | (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args); |
| 304 | } |
| 305 | |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 306 | uint32_t InstrumentPass::GenDebugDirectRead( |
| 307 | const std::vector<uint32_t>& offset_ids, InstructionBuilder* builder) { |
| 308 | // Call debug input function. Pass func_idx and offset ids as args. |
| 309 | uint32_t off_id_cnt = static_cast<uint32_t>(offset_ids.size()); |
| 310 | uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt); |
| 311 | std::vector<uint32_t> args = {input_func_id}; |
| 312 | (void)args.insert(args.end(), offset_ids.begin(), offset_ids.end()); |
| 313 | return builder->AddNaryOp(GetUintId(), SpvOpFunctionCall, args)->result_id(); |
| 314 | } |
| 315 | |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 316 | bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const { |
| 317 | return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage; |
| 318 | } |
| 319 | |
| 320 | void InstrumentPass::CloneSameBlockOps( |
| 321 | std::unique_ptr<Instruction>* inst, |
| 322 | std::unordered_map<uint32_t, uint32_t>* same_blk_post, |
| 323 | std::unordered_map<uint32_t, Instruction*>* same_blk_pre, |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 324 | BasicBlock* block_ptr) { |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 325 | (*inst)->ForEachInId( |
| 326 | [&same_blk_post, &same_blk_pre, &block_ptr, this](uint32_t* iid) { |
| 327 | const auto map_itr = (*same_blk_post).find(*iid); |
| 328 | if (map_itr == (*same_blk_post).end()) { |
| 329 | const auto map_itr2 = (*same_blk_pre).find(*iid); |
| 330 | if (map_itr2 != (*same_blk_pre).end()) { |
| 331 | // Clone pre-call same-block ops, map result id. |
| 332 | const Instruction* in_inst = map_itr2->second; |
| 333 | std::unique_ptr<Instruction> sb_inst(in_inst->Clone(context())); |
| 334 | CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr); |
| 335 | const uint32_t rid = sb_inst->result_id(); |
| 336 | const uint32_t nid = this->TakeNextId(); |
| 337 | get_decoration_mgr()->CloneDecorations(rid, nid); |
| 338 | sb_inst->SetResultId(nid); |
| 339 | (*same_blk_post)[rid] = nid; |
| 340 | *iid = nid; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 341 | block_ptr->AddInstruction(std::move(sb_inst)); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 342 | } |
| 343 | } else { |
| 344 | // Reset same-block op operand. |
| 345 | *iid = map_itr->second; |
| 346 | } |
| 347 | }); |
| 348 | } |
| 349 | |
| 350 | void InstrumentPass::UpdateSucceedingPhis( |
| 351 | std::vector<std::unique_ptr<BasicBlock>>& new_blocks) { |
| 352 | const auto first_blk = new_blocks.begin(); |
| 353 | const auto last_blk = new_blocks.end() - 1; |
| 354 | const uint32_t first_id = (*first_blk)->id(); |
| 355 | const uint32_t last_id = (*last_blk)->id(); |
| 356 | const BasicBlock& const_last_block = *last_blk->get(); |
| 357 | const_last_block.ForEachSuccessorLabel( |
| 358 | [&first_id, &last_id, this](const uint32_t succ) { |
| 359 | BasicBlock* sbp = this->id2block_[succ]; |
| 360 | sbp->ForEachPhiInst([&first_id, &last_id, this](Instruction* phi) { |
| 361 | bool changed = false; |
| 362 | phi->ForEachInId([&first_id, &last_id, &changed](uint32_t* id) { |
| 363 | if (*id == first_id) { |
| 364 | *id = last_id; |
| 365 | changed = true; |
| 366 | } |
| 367 | }); |
| 368 | if (changed) get_def_use_mgr()->AnalyzeInstUse(phi); |
| 369 | }); |
| 370 | }); |
| 371 | } |
| 372 | |
| 373 | // Return id for output buffer uint ptr type |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 374 | uint32_t InstrumentPass::GetBufferUintPtrId() { |
| 375 | if (buffer_uint_ptr_id_ == 0) { |
| 376 | buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType( |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 377 | GetUintId(), SpvStorageClassStorageBuffer); |
| 378 | } |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 379 | return buffer_uint_ptr_id_; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 380 | } |
| 381 | |
| 382 | uint32_t InstrumentPass::GetOutputBufferBinding() { |
| 383 | switch (validation_id_) { |
| 384 | case kInstValidationIdBindless: |
| 385 | return kDebugOutputBindingStream; |
| 386 | default: |
| 387 | assert(false && "unexpected validation id"); |
| 388 | } |
| 389 | return 0; |
| 390 | } |
| 391 | |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 392 | uint32_t InstrumentPass::GetInputBufferBinding() { |
| 393 | switch (validation_id_) { |
| 394 | case kInstValidationIdBindless: |
| 395 | return kDebugInputBindingBindless; |
| 396 | default: |
| 397 | assert(false && "unexpected validation id"); |
| 398 | } |
| 399 | return 0; |
| 400 | } |
| 401 | |
| 402 | analysis::Type* InstrumentPass::GetUintRuntimeArrayType( |
| 403 | analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) { |
| 404 | if (uint_rarr_ty_ == nullptr) { |
| 405 | analysis::Integer uint_ty(32, false); |
| 406 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
| 407 | analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty); |
| 408 | uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp); |
| 409 | uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_); |
| 410 | // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of |
| 411 | // a block, and will therefore be decorated with an ArrayStride. Therefore |
| 412 | // the undecorated type returned here will not be pre-existing and can |
| 413 | // safely be decorated. Since this type is now decorated, it is out of |
| 414 | // sync with the TypeManager and therefore the TypeManager must be |
| 415 | // invalidated after this pass. |
| 416 | assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 && |
| 417 | "used RuntimeArray type returned"); |
| 418 | deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u); |
| 419 | } |
| 420 | return uint_rarr_ty_; |
| 421 | } |
| 422 | |
| 423 | void InstrumentPass::AddStorageBufferExt() { |
| 424 | if (storage_buffer_ext_defined_) return; |
| 425 | if (!get_feature_mgr()->HasExtension(kSPV_KHR_storage_buffer_storage_class)) { |
| 426 | const std::string ext_name("SPV_KHR_storage_buffer_storage_class"); |
| 427 | const auto num_chars = ext_name.size(); |
| 428 | // Compute num words, accommodate the terminating null character. |
| 429 | const auto num_words = (num_chars + 1 + 3) / 4; |
| 430 | std::vector<uint32_t> ext_words(num_words, 0u); |
| 431 | std::memcpy(ext_words.data(), ext_name.data(), num_chars); |
| 432 | context()->AddExtension(std::unique_ptr<Instruction>( |
| 433 | new Instruction(context(), SpvOpExtension, 0u, 0u, |
| 434 | {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); |
| 435 | } |
| 436 | storage_buffer_ext_defined_ = true; |
| 437 | } |
| 438 | |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 439 | // Return id for output buffer |
| 440 | uint32_t InstrumentPass::GetOutputBufferId() { |
| 441 | if (output_buffer_id_ == 0) { |
| 442 | // If not created yet, create one |
| 443 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); |
| 444 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 445 | analysis::Type* reg_uint_rarr_ty = |
| 446 | GetUintRuntimeArrayType(deco_mgr, type_mgr); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 447 | analysis::Integer uint_ty(32, false); |
| 448 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 449 | analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty}); |
| 450 | analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); |
| 451 | uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); |
| 452 | // By the Vulkan spec, a pre-existing struct containing a RuntimeArray |
| 453 | // must be a block, and will therefore be decorated with Block. Therefore |
| 454 | // the undecorated type returned here will not be pre-existing and can |
| 455 | // safely be decorated. Since this type is now decorated, it is out of |
| 456 | // sync with the TypeManager and therefore the TypeManager must be |
| 457 | // invalidated after this pass. |
| 458 | assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 && |
| 459 | "used struct type returned"); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 460 | deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock); |
| 461 | deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, |
| 462 | SpvDecorationOffset, 0); |
| 463 | deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset, |
| 464 | SpvDecorationOffset, 4); |
| 465 | uint32_t obufTyPtrId_ = |
| 466 | type_mgr->FindPointerToType(obufTyId, SpvStorageClassStorageBuffer); |
| 467 | output_buffer_id_ = TakeNextId(); |
| 468 | std::unique_ptr<Instruction> newVarOp(new Instruction( |
| 469 | context(), SpvOpVariable, obufTyPtrId_, output_buffer_id_, |
| 470 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
| 471 | {SpvStorageClassStorageBuffer}}})); |
| 472 | context()->AddGlobalValue(std::move(newVarOp)); |
| 473 | deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet, |
| 474 | desc_set_); |
| 475 | deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding, |
| 476 | GetOutputBufferBinding()); |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 477 | AddStorageBufferExt(); |
| 478 | if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { |
| 479 | // Add the new buffer to all entry points. |
| 480 | for (auto& entry : get_module()->entry_points()) { |
| 481 | entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}}); |
| 482 | context()->AnalyzeUses(&entry); |
| 483 | } |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 484 | } |
| 485 | } |
| 486 | return output_buffer_id_; |
| 487 | } |
| 488 | |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 489 | uint32_t InstrumentPass::GetInputBufferId() { |
| 490 | if (input_buffer_id_ == 0) { |
| 491 | // If not created yet, create one |
| 492 | analysis::DecorationManager* deco_mgr = get_decoration_mgr(); |
| 493 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 494 | analysis::Type* reg_uint_rarr_ty = |
| 495 | GetUintRuntimeArrayType(deco_mgr, type_mgr); |
| 496 | analysis::Struct buf_ty({reg_uint_rarr_ty}); |
| 497 | analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); |
| 498 | uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); |
| 499 | // By the Vulkan spec, a pre-existing struct containing a RuntimeArray |
| 500 | // must be a block, and will therefore be decorated with Block. Therefore |
| 501 | // the undecorated type returned here will not be pre-existing and can |
| 502 | // safely be decorated. Since this type is now decorated, it is out of |
| 503 | // sync with the TypeManager and therefore the TypeManager must be |
| 504 | // invalidated after this pass. |
| 505 | assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 && |
| 506 | "used struct type returned"); |
| 507 | deco_mgr->AddDecoration(ibufTyId, SpvDecorationBlock); |
| 508 | deco_mgr->AddMemberDecoration(ibufTyId, 0, SpvDecorationOffset, 0); |
| 509 | uint32_t ibufTyPtrId_ = |
| 510 | type_mgr->FindPointerToType(ibufTyId, SpvStorageClassStorageBuffer); |
| 511 | input_buffer_id_ = TakeNextId(); |
| 512 | std::unique_ptr<Instruction> newVarOp(new Instruction( |
| 513 | context(), SpvOpVariable, ibufTyPtrId_, input_buffer_id_, |
| 514 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
| 515 | {SpvStorageClassStorageBuffer}}})); |
| 516 | context()->AddGlobalValue(std::move(newVarOp)); |
| 517 | deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet, |
| 518 | desc_set_); |
| 519 | deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding, |
| 520 | GetInputBufferBinding()); |
| 521 | AddStorageBufferExt(); |
| 522 | if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { |
| 523 | // Add the new buffer to all entry points. |
| 524 | for (auto& entry : get_module()->entry_points()) { |
| 525 | entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}}); |
| 526 | context()->AnalyzeUses(&entry); |
| 527 | } |
| 528 | } |
| 529 | } |
| 530 | return input_buffer_id_; |
| 531 | } |
| 532 | |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 533 | uint32_t InstrumentPass::GetVec4FloatId() { |
| 534 | if (v4float_id_ == 0) { |
| 535 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 536 | analysis::Float float_ty(32); |
| 537 | analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); |
| 538 | analysis::Vector v4float_ty(reg_float_ty, 4); |
| 539 | analysis::Type* reg_v4float_ty = type_mgr->GetRegisteredType(&v4float_ty); |
| 540 | v4float_id_ = type_mgr->GetTypeInstruction(reg_v4float_ty); |
| 541 | } |
| 542 | return v4float_id_; |
| 543 | } |
| 544 | |
| 545 | uint32_t InstrumentPass::GetUintId() { |
| 546 | if (uint_id_ == 0) { |
| 547 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 548 | analysis::Integer uint_ty(32, false); |
| 549 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
| 550 | uint_id_ = type_mgr->GetTypeInstruction(reg_uint_ty); |
| 551 | } |
| 552 | return uint_id_; |
| 553 | } |
| 554 | |
| 555 | uint32_t InstrumentPass::GetVec4UintId() { |
| 556 | if (v4uint_id_ == 0) { |
| 557 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 558 | analysis::Integer uint_ty(32, false); |
| 559 | analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); |
| 560 | analysis::Vector v4uint_ty(reg_uint_ty, 4); |
| 561 | analysis::Type* reg_v4uint_ty = type_mgr->GetRegisteredType(&v4uint_ty); |
| 562 | v4uint_id_ = type_mgr->GetTypeInstruction(reg_v4uint_ty); |
| 563 | } |
| 564 | return v4uint_id_; |
| 565 | } |
| 566 | |
| 567 | uint32_t InstrumentPass::GetBoolId() { |
| 568 | if (bool_id_ == 0) { |
| 569 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 570 | analysis::Bool bool_ty; |
| 571 | analysis::Type* reg_bool_ty = type_mgr->GetRegisteredType(&bool_ty); |
| 572 | bool_id_ = type_mgr->GetTypeInstruction(reg_bool_ty); |
| 573 | } |
| 574 | return bool_id_; |
| 575 | } |
| 576 | |
| 577 | uint32_t InstrumentPass::GetVoidId() { |
| 578 | if (void_id_ == 0) { |
| 579 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 580 | analysis::Void void_ty; |
| 581 | analysis::Type* reg_void_ty = type_mgr->GetRegisteredType(&void_ty); |
| 582 | void_id_ = type_mgr->GetTypeInstruction(reg_void_ty); |
| 583 | } |
| 584 | return void_id_; |
| 585 | } |
| 586 | |
| 587 | uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, |
| 588 | uint32_t val_spec_param_cnt) { |
| 589 | // Total param count is common params plus validation-specific |
| 590 | // params |
| 591 | uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt; |
| 592 | if (output_func_id_ == 0) { |
| 593 | // Create function |
| 594 | output_func_id_ = TakeNextId(); |
| 595 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 596 | std::vector<const analysis::Type*> param_types; |
| 597 | for (uint32_t c = 0; c < param_cnt; ++c) |
| 598 | param_types.push_back(type_mgr->GetType(GetUintId())); |
| 599 | analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types); |
| 600 | analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); |
| 601 | std::unique_ptr<Instruction> func_inst(new Instruction( |
| 602 | get_module()->context(), SpvOpFunction, GetVoidId(), output_func_id_, |
| 603 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
| 604 | {SpvFunctionControlMaskNone}}, |
| 605 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, |
| 606 | {type_mgr->GetTypeInstruction(reg_func_ty)}}})); |
| 607 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); |
| 608 | std::unique_ptr<Function> output_func = |
| 609 | MakeUnique<Function>(std::move(func_inst)); |
| 610 | // Add parameters |
| 611 | std::vector<uint32_t> param_vec; |
| 612 | for (uint32_t c = 0; c < param_cnt; ++c) { |
| 613 | uint32_t pid = TakeNextId(); |
| 614 | param_vec.push_back(pid); |
| 615 | std::unique_ptr<Instruction> param_inst( |
| 616 | new Instruction(get_module()->context(), SpvOpFunctionParameter, |
| 617 | GetUintId(), pid, {})); |
| 618 | get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); |
| 619 | output_func->AddParameter(std::move(param_inst)); |
| 620 | } |
| 621 | // Create first block |
| 622 | uint32_t test_blk_id = TakeNextId(); |
| 623 | std::unique_ptr<Instruction> test_label(NewLabel(test_blk_id)); |
| 624 | std::unique_ptr<BasicBlock> new_blk_ptr = |
| 625 | MakeUnique<BasicBlock>(std::move(test_label)); |
| 626 | InstructionBuilder builder( |
| 627 | context(), &*new_blk_ptr, |
| 628 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); |
| 629 | // Gen test if debug output buffer size will not be exceeded. |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 630 | uint32_t val_spec_offset = |
| 631 | (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt; |
| 632 | uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 633 | uint32_t buf_id = GetOutputBufferId(); |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 634 | uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 635 | Instruction* obuf_curr_sz_ac_inst = |
| 636 | builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, |
| 637 | builder.GetUintConstantId(kDebugOutputSizeOffset)); |
| 638 | // Fetch the current debug buffer written size atomically, adding the |
| 639 | // size of the record to be written. |
| 640 | uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz); |
| 641 | uint32_t mask_none_id = builder.GetUintConstantId(SpvMemoryAccessMaskNone); |
| 642 | uint32_t scope_invok_id = builder.GetUintConstantId(SpvScopeInvocation); |
| 643 | Instruction* obuf_curr_sz_inst = builder.AddQuadOp( |
| 644 | GetUintId(), SpvOpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(), |
| 645 | scope_invok_id, mask_none_id, obuf_record_sz_id); |
| 646 | uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id(); |
| 647 | // Compute new written size |
| 648 | Instruction* obuf_new_sz_inst = |
| 649 | builder.AddBinaryOp(GetUintId(), SpvOpIAdd, obuf_curr_sz_id, |
| 650 | builder.GetUintConstantId(obuf_record_sz)); |
| 651 | // Fetch the data bound |
| 652 | Instruction* obuf_bnd_inst = |
| 653 | builder.AddIdLiteralOp(GetUintId(), SpvOpArrayLength, |
| 654 | GetOutputBufferId(), kDebugOutputDataOffset); |
| 655 | // Test that new written size is less than or equal to debug output |
| 656 | // data bound |
| 657 | Instruction* obuf_safe_inst = builder.AddBinaryOp( |
| 658 | GetBoolId(), SpvOpULessThanEqual, obuf_new_sz_inst->result_id(), |
| 659 | obuf_bnd_inst->result_id()); |
| 660 | uint32_t merge_blk_id = TakeNextId(); |
| 661 | uint32_t write_blk_id = TakeNextId(); |
| 662 | std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); |
| 663 | std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id)); |
| 664 | (void)builder.AddConditionalBranch(obuf_safe_inst->result_id(), |
| 665 | write_blk_id, merge_blk_id, merge_blk_id, |
| 666 | SpvSelectionControlMaskNone); |
| 667 | // Close safety test block and gen write block |
| 668 | new_blk_ptr->SetParent(&*output_func); |
| 669 | output_func->AddBasicBlock(std::move(new_blk_ptr)); |
| 670 | new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label)); |
| 671 | builder.SetInsertPoint(&*new_blk_ptr); |
| 672 | // Generate common and stage-specific debug record members |
| 673 | GenCommonStreamWriteCode(obuf_record_sz, param_vec[kInstCommonParamInstIdx], |
| 674 | stage_idx, obuf_curr_sz_id, &builder); |
| 675 | GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder); |
| 676 | // Gen writes of validation specific data |
| 677 | for (uint32_t i = 0; i < val_spec_param_cnt; ++i) { |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 678 | GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i, |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 679 | param_vec[kInstCommonParamCnt + i], &builder); |
| 680 | } |
| 681 | // Close write block and gen merge block |
| 682 | (void)builder.AddBranch(merge_blk_id); |
| 683 | new_blk_ptr->SetParent(&*output_func); |
| 684 | output_func->AddBasicBlock(std::move(new_blk_ptr)); |
| 685 | new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); |
| 686 | builder.SetInsertPoint(&*new_blk_ptr); |
| 687 | // Close merge block and function and add function to module |
| 688 | (void)builder.AddNullaryOp(0, SpvOpReturn); |
| 689 | new_blk_ptr->SetParent(&*output_func); |
| 690 | output_func->AddBasicBlock(std::move(new_blk_ptr)); |
| 691 | std::unique_ptr<Instruction> func_end_inst( |
| 692 | new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); |
| 693 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); |
| 694 | output_func->SetFunctionEnd(std::move(func_end_inst)); |
| 695 | context()->AddFunction(std::move(output_func)); |
| 696 | output_func_param_cnt_ = param_cnt; |
| 697 | } |
| 698 | assert(param_cnt == output_func_param_cnt_ && "bad arg count"); |
| 699 | return output_func_id_; |
| 700 | } |
| 701 | |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 702 | uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { |
| 703 | uint32_t func_id = param2input_func_id_[param_cnt]; |
| 704 | if (func_id != 0) return func_id; |
| 705 | // Create input function for param_cnt |
| 706 | func_id = TakeNextId(); |
| 707 | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
| 708 | std::vector<const analysis::Type*> param_types; |
| 709 | for (uint32_t c = 0; c < param_cnt; ++c) |
| 710 | param_types.push_back(type_mgr->GetType(GetUintId())); |
| 711 | analysis::Function func_ty(type_mgr->GetType(GetUintId()), param_types); |
| 712 | analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); |
| 713 | std::unique_ptr<Instruction> func_inst(new Instruction( |
| 714 | get_module()->context(), SpvOpFunction, GetUintId(), func_id, |
| 715 | {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, |
| 716 | {SpvFunctionControlMaskNone}}, |
| 717 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, |
| 718 | {type_mgr->GetTypeInstruction(reg_func_ty)}}})); |
| 719 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); |
| 720 | std::unique_ptr<Function> input_func = |
| 721 | MakeUnique<Function>(std::move(func_inst)); |
| 722 | // Add parameters |
| 723 | std::vector<uint32_t> param_vec; |
| 724 | for (uint32_t c = 0; c < param_cnt; ++c) { |
| 725 | uint32_t pid = TakeNextId(); |
| 726 | param_vec.push_back(pid); |
| 727 | std::unique_ptr<Instruction> param_inst(new Instruction( |
| 728 | get_module()->context(), SpvOpFunctionParameter, GetUintId(), pid, {})); |
| 729 | get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); |
| 730 | input_func->AddParameter(std::move(param_inst)); |
| 731 | } |
| 732 | // Create block |
| 733 | uint32_t blk_id = TakeNextId(); |
| 734 | std::unique_ptr<Instruction> blk_label(NewLabel(blk_id)); |
| 735 | std::unique_ptr<BasicBlock> new_blk_ptr = |
| 736 | MakeUnique<BasicBlock>(std::move(blk_label)); |
| 737 | InstructionBuilder builder( |
| 738 | context(), &*new_blk_ptr, |
| 739 | IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); |
| 740 | // For each offset parameter, generate new offset with parameter, adding last |
| 741 | // loaded value if it exists, and load value from input buffer at new offset. |
| 742 | // Return last loaded value. |
| 743 | uint32_t buf_id = GetInputBufferId(); |
| 744 | uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); |
| 745 | uint32_t last_value_id = 0; |
| 746 | for (uint32_t p = 0; p < param_cnt; ++p) { |
| 747 | uint32_t offset_id; |
| 748 | if (p == 0) { |
| 749 | offset_id = param_vec[0]; |
| 750 | } else { |
| 751 | Instruction* offset_inst = builder.AddBinaryOp( |
| 752 | GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]); |
| 753 | offset_id = offset_inst->result_id(); |
| 754 | } |
| 755 | Instruction* ac_inst = builder.AddTernaryOp( |
| 756 | buf_uint_ptr_id, SpvOpAccessChain, buf_id, |
| 757 | builder.GetUintConstantId(kDebugInputDataOffset), offset_id); |
| 758 | Instruction* load_inst = |
| 759 | builder.AddUnaryOp(GetUintId(), SpvOpLoad, ac_inst->result_id()); |
| 760 | last_value_id = load_inst->result_id(); |
| 761 | } |
| 762 | (void)builder.AddInstruction(MakeUnique<Instruction>( |
| 763 | context(), SpvOpReturnValue, 0, 0, |
| 764 | std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {last_value_id}}})); |
| 765 | // Close block and function and add function to module |
| 766 | new_blk_ptr->SetParent(&*input_func); |
| 767 | input_func->AddBasicBlock(std::move(new_blk_ptr)); |
| 768 | std::unique_ptr<Instruction> func_end_inst( |
| 769 | new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); |
| 770 | get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); |
| 771 | input_func->SetFunctionEnd(std::move(func_end_inst)); |
| 772 | context()->AddFunction(std::move(input_func)); |
| 773 | param2input_func_id_[param_cnt] = func_id; |
| 774 | return func_id; |
| 775 | } |
| 776 | |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 777 | bool InstrumentPass::InstrumentFunction(Function* func, uint32_t stage_idx, |
| 778 | InstProcessFunction& pfn) { |
| 779 | bool modified = false; |
| 780 | // Compute function index |
| 781 | uint32_t function_idx = 0; |
| 782 | for (auto fii = get_module()->begin(); fii != get_module()->end(); ++fii) { |
| 783 | if (&*fii == func) break; |
| 784 | ++function_idx; |
| 785 | } |
| 786 | std::vector<std::unique_ptr<BasicBlock>> new_blks; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 787 | // Using block iterators here because of block erasures and insertions. |
| 788 | for (auto bi = func->begin(); bi != func->end(); ++bi) { |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 789 | for (auto ii = bi->begin(); ii != bi->end();) { |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 790 | // Generate instrumentation if warranted |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 791 | pfn(ii, bi, stage_idx, &new_blks); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 792 | if (new_blks.size() == 0) { |
| 793 | ++ii; |
| 794 | continue; |
| 795 | } |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 796 | // Add new blocks to label id map |
| 797 | for (auto& blk : new_blks) id2block_[blk->id()] = &*blk; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 798 | // If there are new blocks we know there will always be two or |
| 799 | // more, so update succeeding phis with label of new last block. |
| 800 | size_t newBlocksSize = new_blks.size(); |
| 801 | assert(newBlocksSize > 1); |
| 802 | UpdateSucceedingPhis(new_blks); |
| 803 | // Replace original block with new block(s) |
| 804 | bi = bi.Erase(); |
| 805 | for (auto& bb : new_blks) { |
| 806 | bb->SetParent(func); |
| 807 | } |
| 808 | bi = bi.InsertBefore(&new_blks); |
| 809 | // Reset block iterator to last new block |
| 810 | for (size_t i = 0; i < newBlocksSize - 1; i++) ++bi; |
| 811 | modified = true; |
| 812 | // Restart instrumenting at beginning of last new block, |
| 813 | // but skip over any new phi or copy instruction. |
| 814 | ii = bi->begin(); |
| 815 | if (ii->opcode() == SpvOpPhi || ii->opcode() == SpvOpCopyObject) ++ii; |
| 816 | new_blks.clear(); |
| 817 | } |
| 818 | } |
| 819 | return modified; |
| 820 | } |
| 821 | |
| 822 | bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn, |
| 823 | std::queue<uint32_t>* roots, |
| 824 | uint32_t stage_idx) { |
| 825 | bool modified = false; |
| 826 | std::unordered_set<uint32_t> done; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 827 | // Don't process input and output functions |
| 828 | for (auto& ifn : param2input_func_id_) done.insert(ifn.second); |
| 829 | if (output_func_id_ != 0) done.insert(output_func_id_); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 830 | // Process all functions from roots |
| 831 | while (!roots->empty()) { |
| 832 | const uint32_t fi = roots->front(); |
| 833 | roots->pop(); |
| 834 | if (done.insert(fi).second) { |
| 835 | Function* fn = id2function_.at(fi); |
| 836 | // Add calls first so we don't add new output function |
| 837 | context()->AddCalls(fn, roots); |
| 838 | modified = InstrumentFunction(fn, stage_idx, pfn) || modified; |
| 839 | } |
| 840 | } |
| 841 | return modified; |
| 842 | } |
| 843 | |
| 844 | bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { |
| 845 | // Make sure all entry points have the same execution model. Do not |
| 846 | // instrument if they do not. |
| 847 | // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module |
| 848 | // can contain entry points with different execution models, although |
| 849 | // such modules will likely be rare as GLSL and HLSL are geared toward |
| 850 | // one model per module. In such cases we will need |
| 851 | // to clone any functions which are in the call trees of entrypoints |
| 852 | // with differing execution models. |
| 853 | uint32_t ecnt = 0; |
| 854 | uint32_t stage = SpvExecutionModelMax; |
| 855 | for (auto& e : get_module()->entry_points()) { |
| 856 | if (ecnt == 0) |
| 857 | stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx); |
| 858 | else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != stage) |
| 859 | return false; |
| 860 | ++ecnt; |
| 861 | } |
| 862 | // Only supporting vertex, fragment and compute shaders at the moment. |
| 863 | // TODO(greg-lunarg): Handle all stages. |
| 864 | if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment && |
| 865 | stage != SpvExecutionModelGeometry && |
| 866 | stage != SpvExecutionModelGLCompute && |
| 867 | stage != SpvExecutionModelTessellationControl && |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 868 | stage != SpvExecutionModelTessellationEvaluation && |
| 869 | stage != SpvExecutionModelRayGenerationNV && |
| 870 | stage != SpvExecutionModelIntersectionNV && |
| 871 | stage != SpvExecutionModelAnyHitNV && |
| 872 | stage != SpvExecutionModelClosestHitNV && |
| 873 | stage != SpvExecutionModelMissNV && stage != SpvExecutionModelCallableNV) |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 874 | return false; |
| 875 | // Add together the roots of all entry points |
| 876 | std::queue<uint32_t> roots; |
| 877 | for (auto& e : get_module()->entry_points()) { |
| 878 | roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); |
| 879 | } |
| 880 | bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage); |
| 881 | return modified; |
| 882 | } |
| 883 | |
| 884 | void InstrumentPass::InitializeInstrument() { |
| 885 | output_buffer_id_ = 0; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 886 | buffer_uint_ptr_id_ = 0; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 887 | output_func_id_ = 0; |
| 888 | output_func_param_cnt_ = 0; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 889 | input_buffer_id_ = 0; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 890 | v4float_id_ = 0; |
| 891 | uint_id_ = 0; |
| 892 | v4uint_id_ = 0; |
| 893 | bool_id_ = 0; |
| 894 | void_id_ = 0; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 895 | storage_buffer_ext_defined_ = false; |
| 896 | uint_rarr_ty_ = nullptr; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 897 | |
| 898 | // clear collections |
| 899 | id2function_.clear(); |
| 900 | id2block_.clear(); |
| 901 | |
| 902 | // Initialize function and block maps. |
| 903 | for (auto& fn : *get_module()) { |
| 904 | id2function_[fn.result_id()] = &fn; |
| 905 | for (auto& blk : fn) { |
| 906 | id2block_[blk.id()] = &blk; |
| 907 | } |
| 908 | } |
| 909 | |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 910 | // Remember original instruction offsets |
| 911 | uint32_t module_offset = 0; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 912 | Module* module = get_module(); |
| 913 | for (auto& i : context()->capabilities()) { |
| 914 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 915 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 916 | } |
| 917 | for (auto& i : module->extensions()) { |
| 918 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 919 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 920 | } |
| 921 | for (auto& i : module->ext_inst_imports()) { |
| 922 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 923 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 924 | } |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 925 | ++module_offset; // memory_model |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 926 | for (auto& i : module->entry_points()) { |
| 927 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 928 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 929 | } |
| 930 | for (auto& i : module->execution_modes()) { |
| 931 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 932 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 933 | } |
| 934 | for (auto& i : module->debugs1()) { |
| 935 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 936 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 937 | } |
| 938 | for (auto& i : module->debugs2()) { |
| 939 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 940 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 941 | } |
| 942 | for (auto& i : module->debugs3()) { |
| 943 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 944 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 945 | } |
| 946 | for (auto& i : module->annotations()) { |
| 947 | (void)i; |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 948 | ++module_offset; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 949 | } |
| 950 | for (auto& i : module->types_values()) { |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 951 | module_offset += 1; |
| 952 | module_offset += static_cast<uint32_t>(i.dbg_line_insts().size()); |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 953 | } |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 954 | |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 955 | auto curr_fn = get_module()->begin(); |
| 956 | for (; curr_fn != get_module()->end(); ++curr_fn) { |
| 957 | // Count function instruction |
| 958 | module_offset += 1; |
| 959 | curr_fn->ForEachParam( |
| 960 | [&module_offset](const Instruction*) { module_offset += 1; }, true); |
| 961 | for (auto& blk : *curr_fn) { |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 962 | // Count label |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 963 | module_offset += 1; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 964 | for (auto& inst : blk) { |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 965 | module_offset += static_cast<uint32_t>(inst.dbg_line_insts().size()); |
| 966 | uid2offset_[inst.unique_id()] = module_offset; |
| 967 | module_offset += 1; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 968 | } |
| 969 | } |
Ben Clayton | b73b760 | 2019-07-29 13:56:13 +0100 | [diff] [blame^] | 970 | // Count function end instruction |
| 971 | module_offset += 1; |
Chris Forbes | cc5697f | 2019-01-30 11:54:08 -0800 | [diff] [blame] | 972 | } |
| 973 | } |
| 974 | |
| 975 | } // namespace opt |
| 976 | } // namespace spvtools |